From 3044697baa8e098a31f8049194d7cf1890363cb2 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sat, 5 Mar 2016 19:02:50 -0800 Subject: [PATCH 001/363] Prefer the first, not last, of any env var duplicates If envp contains duplicate environment variables, use the first value, not the last value. Fixes #2784. --- src/env.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/env.cpp b/src/env.cpp index e6deec52a..28ea51b05 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -449,12 +449,16 @@ void env_init(const struct config_paths_t *paths /* or NULL */) is to insert valid data */ - /* - Import environment variables - */ - for (char **p = (environ ? environ : __environ); p && *p; p++) + /* Import environment variables. Walk backwards so that the first one out of any duplicates wins (#2784) */ + const char * const * envp = (environ ? environ : __environ); + size_t i = 0; + while (envp && envp[i]) { - const wcstring key_and_val = str2wcstring(*p); //like foo=bar + i++; + } + while (i--) + { + const wcstring key_and_val = str2wcstring(envp[i]); //like foo=bar size_t eql = key_and_val.find(L'='); if (eql == wcstring::npos) { From a37d4d809e067e54dfaec74888bd668f1a1727d1 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sat, 5 Mar 2016 19:07:00 -0800 Subject: [PATCH 002/363] Save a few string allocations when importing environment variables --- src/env.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/env.cpp b/src/env.cpp index 28ea51b05..c24280bcc 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -450,6 +450,7 @@ void env_init(const struct config_paths_t *paths /* or NULL */) */ /* Import environment variables. Walk backwards so that the first one out of any duplicates wins (#2784) */ + wcstring key, val; const char * const * envp = (environ ? environ : __environ); size_t i = 0; while (envp && envp[i]) @@ -468,9 +469,9 @@ void env_init(const struct config_paths_t *paths /* or NULL */) } else { - wcstring key = key_and_val.substr(0, eql); + key.assign(key_and_val, 0, eql); if (is_read_only(key) || is_electric(key)) continue; - wcstring val = key_and_val.substr(eql + 1); + val.assign(key_and_val, eql + 1, wcstring::npos); if (variable_is_colon_delimited_array(key)) { std::replace(val.begin(), val.end(), L':', ARRAY_SEP); From 6bee85fefa968a632287d5830be6023b90b88068 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sat, 5 Mar 2016 21:50:05 -0800 Subject: [PATCH 003/363] expand_string should not return any results on error Fixes #2796 --- src/expand.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/expand.cpp b/src/expand.cpp index 34e861f64..c2b4258c8 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -1908,13 +1908,15 @@ expand_error_t expand_string(const wcstring &input, std::vector *o output_storage.clear(); } - /* Hack to un-expand tildes (see #647) */ - if (!(flags & EXPAND_SKIP_HOME_DIRECTORIES)) + if (total_result != EXPAND_ERROR) { - unexpand_tildes(input, &completions); + /* Hack to un-expand tildes (see #647) */ + if (!(flags & EXPAND_SKIP_HOME_DIRECTORIES)) + { + unexpand_tildes(input, &completions); + } + out_completions->insert(out_completions->end(), completions.begin(), completions.end()); } - - out_completions->insert(out_completions->end(), completions.begin(), completions.end()); return total_result; } From ed4b78918a944fe3b3a1e05ac553e89d5f1fa411 Mon Sep 17 00:00:00 2001 From: David Adam Date: Sun, 6 Mar 2016 19:24:02 +0800 Subject: [PATCH 004/363] fish.spec: dependency on net-tools/hostname depending on platform Closes #2190. [ci skip] --- fish.spec.in | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fish.spec.in b/fish.spec.in index fda42790b..04006037d 100644 --- a/fish.spec.in +++ b/fish.spec.in @@ -14,10 +14,16 @@ BuildRequires: ncurses-devel gettext gcc-c++ autoconf BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) Requires: bc -Requires: python +Requires: python Requires: which Requires: man +%if 0%{?rhel_version} >= 700 || 0%{?centos_version} >= 700 || 0%{?fedora} +Requires: hostname +%else +Requires: net-tools +%endif + %description fish is a shell geared towards interactive use. Its features are From b4b52b82340985595949238966b68daa710c99b2 Mon Sep 17 00:00:00 2001 From: David Adam Date: Sun, 6 Mar 2016 19:29:32 +0800 Subject: [PATCH 005/363] Don't set locale to en_US.UTF-8 unconditionally. This is an unwise assumption, both for en_US and UTF-8; the fallback C locale should be used instead. --- etc/config.fish | 9 --------- 1 file changed, 9 deletions(-) diff --git a/etc/config.fish b/etc/config.fish index 0683f40e6..f4126d360 100644 --- a/etc/config.fish +++ b/etc/config.fish @@ -8,15 +8,6 @@ if status --is-login - # - # Set some value for LANG if nothing was set before, and this is a - # login shell. - # - - if not set -q LANG >/dev/null - set -gx LANG en_US.UTF-8 - end - # Check for i18n information in # /etc/sysconfig/i18n From f4c14b69a23e8a25c016b6ba65ef2f54b4833e6b Mon Sep 17 00:00:00 2001 From: David Adam Date: Sun, 6 Mar 2016 21:29:09 +0800 Subject: [PATCH 006/363] Update dpkg completions to complete commonly used options Closes #2798. --- share/completions/dpkg.fish | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/share/completions/dpkg.fish b/share/completions/dpkg.fish index c199bdada..d60b6dbad 100644 --- a/share/completions/dpkg.fish +++ b/share/completions/dpkg.fish @@ -1,6 +1,19 @@ complete -c dpkg -s i -l install -d 'Install .deb package' -xa '(__fish_complete_suffix .deb)' -complete -c dpkg -s r -l remove -d 'Remove package' -xa '(__fish_print_packages)' -complete -c dpkg -s P -l purge -d 'Purge package' -xa '(__fish_print_packages)' +complete -c dpkg -l unpack -d 'Unpack .deb package' -xa '(__fish_complete_suffix .deb)' +complete -c dpkg -l configure -d 'Configure package' -xa '(dpkg-query -W -f \'${Package}\n\')' +complete -c dpkg -s r -l remove -d 'Remove package' -xa '(dpkg-query -W -f \'${Package}\n\')' +complete -c dpkg -s P -l purge -d 'Purge package' -xa '(dpkg-query -W -f \'${Package}\n\')' +complete -c dpkg -s V -l verify -d 'Verify contents of package' -xa '(dpkg-query -W -f \'${Package}\n\')' complete -c dpkg -l force-all -d 'Continue on all problems' +# dpkg-deb options +complete -c dpkg -s b -l build -d 'Build package from directory' -xa '(__fish_complete_directories (commandline -ct))' +complete -c dpkg -s c -l contents -d 'List contents of .deb' -xa '(__fish_complete_suffix .deb)' +complete -c dpkg -s I -l info -d 'Show .deb information' -xa '(__fish_complete_suffix .deb)' + +# dpkg-query options +complete -c dpkg -s l -l list -d 'List packages matching pattern' -xa '(dpkg-query -W -f \'${Package}\n\')' +complete -c dpkg -s L -l listfiles -d 'List contents of packages' -xa '(dpkg-query -W -f \'${Package}\n\')' +complete -c dpkg -s s -l status -d 'Print status of package' -xa '(dpkg-query -W -f \'${Package}\n\')' +complete -c dpkg -s S -l search -d 'Search for packages containing file' From 5e09411340b347caf2f81dc65fc9d3a1fefac261 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 6 Mar 2016 15:23:30 +0100 Subject: [PATCH 007/363] Document more bind functions Fixes #2534 as backward-kill-path-component is now documented. --- doc_src/bind.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/doc_src/bind.txt b/doc_src/bind.txt index c56c5c9a7..813026c3b 100644 --- a/doc_src/bind.txt +++ b/doc_src/bind.txt @@ -53,6 +53,8 @@ The following parameters are available: The following special input functions are available: +- `accept-autosuggestion`, accept the current autosuggestion completely + - `backward-char`, moves one character to the left - `backward-bigword`, move one whitespace-delimited word to the left @@ -63,6 +65,8 @@ The following special input functions are available: - `backward-kill-line`, move everything from the beginning of the line to the cursor to the killring +- `backward-kill-path-component`, move one path component to the left of the cursor (everything from the last "/" or whitespace exclusive) to the killring + - `backward-kill-word`, move the word to the left of the cursor to the killring - `backward-word`, move one word to the left @@ -75,6 +79,8 @@ The following special input functions are available: - `complete`, guess the remainder of the current token +- `complete-and-search`, invoke the searchable pager on completion options + - `delete-char`, delete one character to the right of the cursor - `delete-line`, delete the entire line @@ -107,6 +113,12 @@ The following special input functions are available: - `kill-word`, move the next word to the killring +- `suppress-autosuggestion`, remove the current autosuggestion + +- `transpose-chars`, transpose two characters to the left of the cursor + +- `transpose-words`, transpose two words to the left of the cursor + - `upcase-word`, make the current word uppercase - `yank`, insert the latest entry of the killring into the buffer From 6a16bdb808b02977cc99f84b4adae420f99019d2 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Thu, 3 Mar 2016 15:36:17 -0800 Subject: [PATCH 008/363] assume getopt/getopt_long is available There is no longer a good reason to detect whether or not getopt_long() is available. All UNIX implementations we're likely to run on have it. And if we ever find one that doesn't the right thing to do is not fallback to getopt() but to include the getopt_long() source in our package like we do with the pcre2 library. Since it's licensed under LGPL we can legally do so if it becomes necessary. This partially addresses issue #2790. --- configure.ac | 37 ----------------------------------- src/fallback.cpp | 14 -------------- src/fallback.h | 47 --------------------------------------------- src/fish.cpp | 26 ++++++++----------------- src/fish_indent.cpp | 45 ++++++++++++++++++------------------------- src/fish_tests.cpp | 8 -------- 6 files changed, 27 insertions(+), 150 deletions(-) diff --git a/configure.ac b/configure.ac index fe8b3a73c..af4fc5aff 100644 --- a/configure.ac +++ b/configure.ac @@ -752,43 +752,6 @@ else AC_MSG_RESULT(no) fi -# Check if getopt_long exists and works -AC_MSG_CHECKING([if getopt_long exists and works]) -AC_TRY_LINK( - [ - #if HAVE_GETOPT_H - #include - #endif - ], - [ - static struct option - long_options[] = - { - 0, 0, 0, 0 - } - ; - int opt = getopt_long( 0, - 0, - 0, - long_options, - 0 ); - - ], - have_working_getopt_long=yes, - have_working_getopt_long=no -) -if test "$have_working_getopt_long" = yes; then - AC_MSG_RESULT(yes) - AC_DEFINE( - [HAVE_WORKING_GETOPT_LONG], - [1], - [Define to 1 if getopt_long exists and works.] - ) -else - AC_MSG_RESULT(no) -fi - - # Check for Solaris curses tputs having fixed length parameter list. AC_MSG_CHECKING([if we are using non varargs tparm.]) AC_COMPILE_IFELSE( diff --git a/src/fallback.cpp b/src/fallback.cpp index 6b4b8b0a9..7c5f84d23 100644 --- a/src/fallback.cpp +++ b/src/fallback.cpp @@ -1207,20 +1207,6 @@ int killpg(int pgr, int sig) } #endif -#ifndef HAVE_WORKING_GETOPT_LONG - -int getopt_long(int argc, - char * const argv[], - const char *optstring, - const struct option *longopts, - int *longindex) -{ - return getopt(argc, argv, optstring); -} - - -#endif - #ifndef HAVE_BACKTRACE int backtrace(void **buffer, int size) { diff --git a/src/fallback.h b/src/fallback.h index 7d3afa13b..e30444497 100644 --- a/src/fallback.h +++ b/src/fallback.h @@ -413,53 +413,6 @@ extern int _nl_msg_cat_cntr; int killpg(int pgr, int sig); #endif - -#ifndef HAVE_WORKING_GETOPT_LONG - -/** - Struct describing a long getopt option - */ -struct option -{ - /** - Name of option - */ - const char *name; - /** - Flag - */ - int has_arg; - /** - Flag - */ - int *flag; - /** - Return value - */ - int val; -} -; - -#ifndef no_argument -#define no_argument 0 -#endif - -#ifndef required_argument -#define required_argument 1 -#endif - -#ifndef optional_argument -#define optional_argument 2 -#endif - -int getopt_long(int argc, - char * const argv[], - const char *optstring, - const struct option *longopts, - int *longindex); - -#endif - #ifndef HAVE_SYSCONF #define _SC_ARG_MAX 1 diff --git a/src/fish.cpp b/src/fish.cpp index 916102321..048c9cbe3 100644 --- a/src/fish.cpp +++ b/src/fish.cpp @@ -23,6 +23,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA #include "config.h" #include +#include #include #include #include @@ -39,15 +40,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA #include // IWYU pragma: keep - suggests internal header #include #include - -#ifdef HAVE_GETOPT_H -#include -#endif - #include #include "fallback.h" // IWYU pragma: keep - #include "common.h" #include "reader.h" #include "builtin.h" @@ -72,11 +67,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA #define PATH_MAX 1024 #endif -/** - The string describing the single-character options accepted by the main fish binary -*/ -#define GETOPT_STRING "+hilnvc:p:d:" - /* If we are doing profiling, the filename to output to */ static const char *s_profiling_output_filename = NULL; @@ -370,7 +360,8 @@ static int read_init(const struct config_paths_t &paths) */ static int fish_parse_opt(int argc, char **argv, std::vector *cmds) { - const struct option long_options[] = + const char *short_opts = "+hilnvc:p:d:"; + const struct option long_opts[] = { { "command", required_argument, NULL, 'c' }, { "debug-level", required_argument, NULL, 'd' }, @@ -383,17 +374,15 @@ static int fish_parse_opt(int argc, char **argv, std::vector *cmds) { NULL, 0, NULL, 0 } }; - while (1) + int opt; + while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) { - int opt = getopt_long(argc, argv, GETOPT_STRING, long_options, NULL); - if (opt == -1) - break; - switch (opt) { case 0: { - break; + fwprintf(stderr, _(L"getopt_long() unexpectedly returned zero\n")); + exit_without_destructors(127); } case 'c': @@ -462,6 +451,7 @@ static int fish_parse_opt(int argc, char **argv, std::vector *cmds) default: { + // We assume getopt_long() has already emitted a diagnostic msg. exit_without_destructors(1); } diff --git a/src/fish_indent.cpp b/src/fish_indent.cpp index 60ed23343..037d9b773 100644 --- a/src/fish_indent.cpp +++ b/src/fish_indent.cpp @@ -21,13 +21,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA #include "config.h" +#include #include #include #include #include -#ifdef HAVE_GETOPT_H -#include -#endif #include #include #include @@ -297,40 +295,34 @@ int main(int argc, char *argv[]) output_type_ansi, output_type_html } output_type = output_type_plain_text; - - /* Whether to indent (true) or just reformat to one job per line (false) */ bool do_indent = true; - while (1) + const char *short_opts = "+hvi"; + const struct option long_opts[] = { - const struct option long_options[] = - { - { "no-indent", no_argument, 0, 'i' }, - { "help", no_argument, 0, 'h' }, - { "version", no_argument, 0, 'v' }, - { "html", no_argument, 0, 1 }, - { "ansi", no_argument, 0, 2 }, - { 0, 0, 0, 0 } - }; - - int opt_index = 0; - int opt = getopt_long(argc, argv, "hvi", long_options, &opt_index); - if (opt == -1) - break; + { "no-indent", no_argument, NULL, 'i' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "html", no_argument, NULL, 1 }, + { "ansi", no_argument, NULL, 2 }, + { NULL, 0, NULL, 0 } + }; + int opt; + while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) + { switch (opt) { case 0: { - break; + fwprintf(stderr, _(L"getopt_long() unexpectedly returned zero\n")); + exit_without_destructors(127); } case 'h': { print_help("fish_indent", 1); - exit(0); - assert(0 && "Unreachable code reached"); - break; + exit_without_destructors(0); } case 'v': @@ -359,9 +351,10 @@ int main(int argc, char *argv[]) break; } - case '?': + default: { - exit(1); + // We assume getopt_long() has already emitted a diagnostic msg. + exit_without_destructors(1); } } } diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index bcfff0324..dfc346092 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -4,7 +4,6 @@ #include "config.h" - #include #include #include @@ -24,20 +23,13 @@ #include #include #include - -#ifdef HAVE_GETOPT_H -#include -#endif - #include - #include #include #include #include "fallback.h" #include "util.h" - #include "common.h" #include "proc.h" #include "reader.h" From 6288f89bf95273cbce8437cfd6466cf8fc80850b Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 6 Mar 2016 15:28:54 +0100 Subject: [PATCH 009/363] Move code in etc/config.fish to share/config.fish instead add a bit of information on how fish's configuration works for the admin to etc/config.fish. This means that fish is fully functional without /etc, which might be nice for "stateless" systems. --- etc/config.fish | 42 +++++++++++++----------------------------- share/config.fish | 29 +++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 29 deletions(-) diff --git a/etc/config.fish b/etc/config.fish index f4126d360..9be6f0728 100644 --- a/etc/config.fish +++ b/etc/config.fish @@ -1,30 +1,14 @@ -# -# Init file for fish -# - -# -# Some things should only be done for login terminals -# - -if status --is-login - - # Check for i18n information in - # /etc/sysconfig/i18n - - if test -f /etc/sysconfig/i18n - eval (cat /etc/sysconfig/i18n |sed -ne 's/^\([a-zA-Z]*\)=\(.*\)$/set -gx \1 \2;/p') - end - - # - # Put linux consoles in unicode mode. - # - - if test "$TERM" = linux - if expr "$LANG" : ".*\.[Uu][Tt][Ff].*" >/dev/null - if which unicode_start >/dev/null - unicode_start - end - end - end -end +# Put system-wide fish configuration entries here +# or in .fish files in conf.d/ +# Files in conf.d can be overridden by the user +# by files with the same name in $XDG_CONFIG_HOME/fish/conf.d +# This file is run by all fish instances. +# To include configuration only for login shells, use +# if status --is-login +# ... +# end +# To include configuration only for interactive shells, use +# if status --is-interactiv +# ... +# end diff --git a/share/config.fish b/share/config.fish index b7a461c3e..9ed3250a7 100644 --- a/share/config.fish +++ b/share/config.fish @@ -176,3 +176,32 @@ if not set -q __fish_init_2_3_0 set fish_user_abbreviations $fab set -U __fish_init_2_3_0 end + +# +# Some things should only be done for login terminals +# This used to be in etc/config.fish - keep it here to keep the semantics +# + +if status --is-login + + # Check for i18n information in + # /etc/sysconfig/i18n + + if test -f /etc/sysconfig/i18n + string match -r '^[a-zA-Z]*=.*' < /etc/sysconfig/i18n | while read -l line + set -gx (string split '=' -m 1 -- $line | string replace -ra '"([^"]+)"' '$1' | string replace -ra "'([^']+)'" '$1') + end + end + + # + # Put linux consoles in unicode mode. + # + + if test "$TERM" = linux + if string match -qir '\.UTF' -- $LANG + if command -s unicode_start >/dev/null + unicode_start + end + end + end +end From 540bdfcb026dfadf8d6fc8bde91cddc886b2c677 Mon Sep 17 00:00:00 2001 From: lordlycastle Date: Thu, 10 Mar 2016 15:35:39 +0000 Subject: [PATCH 010/363] date and uniq completions for OS X. --- share/completions/date.fish | 33 +++++++++++++++++++++----------- share/completions/uniq.fish | 38 ++++++++++++++++++++++--------------- 2 files changed, 45 insertions(+), 26 deletions(-) diff --git a/share/completions/date.fish b/share/completions/date.fish index 1c93f2352..b7725f199 100644 --- a/share/completions/date.fish +++ b/share/completions/date.fish @@ -1,11 +1,22 @@ -complete -c date -s d -l date --description "Display date described by string" -x -complete -c date -s f -l file --description "Display date for each line in file" -r -complete -c date -s I -l iso-8601 --description "Output in ISO 8601 format" -x -a "date hours minutes seconds" -complete -c date -s s -l set --description "Set time" -x -complete -c date -s R -l rfc-2822 --description "Output RFC-2822 compliant date string" -complete -c date -s r -l reference --description "Display the last modification time of file" -r -complete -c date -s u -l utc --description "Print or set Coordinated Universal Time" -complete -c date -l universal --description "Print or set Coordinated Universal Time" -complete -c date -s h -l help --description "Display help and exit" -complete -c date -s v -l version --description "Display version and exit" - +if date --version > /dev/null ^ /dev/null + complete -c date -s d -l date --description "Display date described by string" -x + complete -c date -s f -l file --description "Display date for each line in file" -r + complete -c date -s I -l iso-8601 --description "Output in ISO 8601 format" -x -a "date hours minutes seconds" + complete -c date -s s -l set --description "Set time" -x + complete -c date -s R -l rfc-2822 --description "Output RFC-2822 compliant date string" + complete -c date -s r -l reference --description "Display the last modification time of file" -r + complete -c date -s u -l utc --description "Print or set Coordinated Universal Time" + complete -c date -l universal --description "Print or set Coordinated Universal Time" + complete -c date -s h -l help --description "Display help and exit" + complete -c date -s v -l version --description "Display version and exit" +else # OS X + complete -c date -s d -d 'Set the kernel\'s value for daylight saving time' -x + complete -c date -s f -d 'Use format string to parse the date provided rather than default format' + complete -c date -s j -d 'Do no try to set the date' + complete -c date -s n -d 'Set the time for current machine only in the local group' + complete -c date -s r -d 'Print date and time represented by SEC since Epoch' -x + complete -c date -s t -d 'Set system\'s value for MINUTES west of GMT' + complete -c date -s u -d 'Display or set the date int UTC time' + complete -c date -s v -d 'Adjust the time component backward(-)/forward(+) according to VAL' -x +end + diff --git a/share/completions/uniq.fish b/share/completions/uniq.fish index 0027d71fc..1e35c9c59 100644 --- a/share/completions/uniq.fish +++ b/share/completions/uniq.fish @@ -1,15 +1,23 @@ -complete -c uniq -s c -l count --description "Print number of occurences" -complete -c uniq -s d -l repeated --description "Only print duplicates" -complete -c uniq -s D -l all-repeated --description "Remove non-duplicate lines" -f -x -a " - none\t'Remove none-duplicate lines' - prepend\t'Remove non-duplicate lines and print an empty line before each non-duplicate' - separate\t'Remove non-duplicate lines and print an empty line between each non-duplicate' -" -complete -c uniq -s f -l skip-fields --description "Avoid comparing first N fields" -r -complete -c uniq -s i -l ignore-case --description "Case insensitive" -complete -c uniq -s s -l skip-chars --description "Avoid comparing first N characters" -r -complete -c uniq -s u -l unique --description "Only print unique lines" -complete -c uniq -s w -l check-chars --description "Compare only specified number of characters" -r -complete -c uniq -l help --description "Display help and exit" -complete -c uniq -l version --description "Display version and exit" - +if uniq --version > /dev/null ^ /dev/null + complete -c uniq -s c -l count --description "Print number of occurences" + complete -c uniq -s d -l repeated --description "Only print duplicates" + complete -c uniq -s D -l all-repeated --description "Remove non-duplicate lines" -f -x -a " + none\t'Remove none-duplicate lines' + prepend\t'Remove non-duplicate lines and print an empty line before each non-duplicate' + separate\t'Remove non-duplicate lines and print an empty line between each non-duplicate' + " + complete -c uniq -s f -l skip-fields --description "Avoid comparing first N fields" -r + complete -c uniq -s i -l ignore-case --description "Case insensitive" + complete -c uniq -s s -l skip-chars --description "Avoid comparing first N characters" -r + complete -c uniq -s u -l unique --description "Only print unique lines" + complete -c uniq -s w -l check-chars --description "Compare only specified number of characters" -r + complete -c uniq -l help --description "Display help and exit" + complete -c uniq -l version --description "Display version and exit" +else # OS X + complete -c uniq -s c -d 'Precede each output line with count of it\'s occurrence' + complete -c uniq -s d -d 'Only print duplicates' + complete -c uniq -s f -d 'Avoid comparing first N fields' -x + complete -c uniq -s s -d 'Avoid comparing fist N characters' -x + complete -c uniq -s u -d 'Only print unique lines' + complete -c uniq -s i -d 'Case insensitive comparision' +end From 46b9f263ac780ae6a921fc24611c75bbec92ebca Mon Sep 17 00:00:00 2001 From: Jeff Kowalski Date: Sat, 12 Mar 2016 11:21:52 -0800 Subject: [PATCH 011/363] Handle return values from fchown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function fchown is annotated with warn_unused_result. As formerly used in the code, it would emit a compiler warning ```warning: ignoring return value of ‘fchown’, declared with attribute warn_unused_result [-Wunused-result]``` This commit notes the return value and emits appropriate error/logging messages if the call fails, creating more traceable results and satisfying the compiler. --- src/env_universal_common.cpp | 9 +++++---- src/history.cpp | 5 ++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/env_universal_common.cpp b/src/env_universal_common.cpp index bee25a1ea..5cd9edb14 100644 --- a/src/env_universal_common.cpp +++ b/src/env_universal_common.cpp @@ -859,22 +859,23 @@ bool env_universal_t::sync(callback_data_list_t *callbacks) success = this->write_to_fd(private_fd, private_file_path); if (! success) UNIVERSAL_LOG("write_to_fd() failed"); } - + if (success) { /* Ensure we maintain ownership and permissions (#2176) */ struct stat sbuf; if (wstat(vars_path, &sbuf) >= 0) { - fchown(private_fd, sbuf.st_uid, sbuf.st_gid); + if (0 > fchown(private_fd, sbuf.st_uid, sbuf.st_gid)) + UNIVERSAL_LOG("fchown() failed"); fchmod(private_fd, sbuf.st_mode); } - + /* Linux by default stores the mtime with low precision, low enough that updates that occur in quick succession may result in the same mtime (even the nanoseconds field). So manually set the mtime of the new file to a high-precision clock. Note that this is only necessary because Linux aggressively reuses inodes, causing the ABA problem; on other platforms we tend to notice the file has changed due to a different inode (or file size!) - + It's probably worth finding a simpler solution to this. The tests ran into this, but it's unlikely to affect users. */ #if HAVE_CLOCK_GETTIME && HAVE_FUTIMENS diff --git a/src/history.cpp b/src/history.cpp index 1bada96d3..022524c35 100644 --- a/src/history.cpp +++ b/src/history.cpp @@ -1453,7 +1453,10 @@ bool history_t::save_internal_via_rewrite() if (wstat(new_name, &sbuf) >= 0) { /* Success */ - fchown(out_fd, sbuf.st_uid, sbuf.st_gid); + if (0 > fchown(out_fd, sbuf.st_uid, sbuf.st_gid)) + { + debug(2, L"Error when changing ownership of history file"); + } fchmod(out_fd, sbuf.st_mode); } From a7012648fe64fe5e501a7439660684f93afcd60d Mon Sep 17 00:00:00 2001 From: Jeff Kowalski Date: Sat, 12 Mar 2016 12:26:01 -0800 Subject: [PATCH 012/363] Improve error handling around fchown Address the feedback from the prior commit: - Change the sense of return value testing to match more common comparison idiom - Test result of fchmod as well as fchown - Change sense of return value testing around wrename as well - Include errno where possible in error message --- src/env_universal_common.cpp | 5 +++-- src/history.cpp | 13 ++++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/env_universal_common.cpp b/src/env_universal_common.cpp index 5cd9edb14..a0b4ebfdc 100644 --- a/src/env_universal_common.cpp +++ b/src/env_universal_common.cpp @@ -866,9 +866,10 @@ bool env_universal_t::sync(callback_data_list_t *callbacks) struct stat sbuf; if (wstat(vars_path, &sbuf) >= 0) { - if (0 > fchown(private_fd, sbuf.st_uid, sbuf.st_gid)) + if (fchown(private_fd, sbuf.st_uid, sbuf.st_gid) == -1) UNIVERSAL_LOG("fchown() failed"); - fchmod(private_fd, sbuf.st_mode); + if (fchmod(private_fd, sbuf.st_mode) == -1) + UNIVERSAL_LOG("fchmod() failed"); } /* Linux by default stores the mtime with low precision, low enough that updates that occur in quick succession may diff --git a/src/history.cpp b/src/history.cpp index 022524c35..75e426387 100644 --- a/src/history.cpp +++ b/src/history.cpp @@ -1453,16 +1453,19 @@ bool history_t::save_internal_via_rewrite() if (wstat(new_name, &sbuf) >= 0) { /* Success */ - if (0 > fchown(out_fd, sbuf.st_uid, sbuf.st_gid)) + if (fchown(out_fd, sbuf.st_uid, sbuf.st_gid) == -1) { - debug(2, L"Error when changing ownership of history file"); + debug(2, L"Error %d when changing ownership of history file", errno); + } + if (fchmod(out_fd, sbuf.st_mode) == -1) + { + debug(2, L"Error %d when changing mode of history file", errno); } - fchmod(out_fd, sbuf.st_mode); } - if (0 > wrename(tmp_name, new_name)) + if (wrename(tmp_name, new_name) == -1) { - debug(2, L"Error when renaming history file"); + debug(2, L"Error %d when renaming history file", errno); } } close(out_fd); From f20e8420a8eb58c4e27509f624d7756d8078937d Mon Sep 17 00:00:00 2001 From: Andreas Nordal Date: Sun, 13 Mar 2016 20:18:37 +0100 Subject: [PATCH 013/363] parse_execution.cpp: Remove line continuations My IDE (Kdevelop 4.7.3) didn't tackle them. --- src/parse_execution.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp index 608857604..c54942878 100644 --- a/src/parse_execution.cpp +++ b/src/parse_execution.cpp @@ -1367,13 +1367,11 @@ parse_execution_result_t parse_execution_context_t::run_1_job(const parse_node_t job_set_flag(j, JOB_FOREGROUND, ! tree.job_should_be_backgrounded(job_node)); - job_set_flag(j, JOB_TERMINAL, job_get_flag(j, JOB_CONTROL) \ - && (!is_subshell && !is_event)); + 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())); + job_set_flag(j, JOB_SKIP_NOTIFICATION, + is_subshell || is_block || is_event || !get_is_interactive()); /* Tell the current block what its job is. This has to happen before we populate it (#1394) */ parser->current_block()->job = j; From 19c13c72f69cd35449add4c07cf9dd6ff7ff0810 Mon Sep 17 00:00:00 2001 From: David Adam Date: Fri, 18 Mar 2016 11:45:48 +0800 Subject: [PATCH 014/363] env_universal_common: use uid_t in geteuid checks The u_int typedef fails to compile on all platforms (e.g. Windows). It is part of the code imported from tmux. Update it to the SUS-standard uid_t. Closes #2821. --- src/env_universal_common.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/env_universal_common.cpp b/src/env_universal_common.cpp index a0b4ebfdc..71fb1667d 100644 --- a/src/env_universal_common.cpp +++ b/src/env_universal_common.cpp @@ -129,7 +129,7 @@ static int check_runtime_path(const char * path) */ struct stat statpath; - u_int uid = geteuid(); + uid_t uid = geteuid(); if (mkdir(path, S_IRWXU) != 0 && errno != EEXIST) return errno; From 03460a3928e7545ebef736b1c0be94e79734c725 Mon Sep 17 00:00:00 2001 From: David Adam Date: Fri, 18 Mar 2016 11:45:48 +0800 Subject: [PATCH 015/363] env_universal_common: use uid_t in geteuid checks The u_int typedef fails to compile on all platforms (e.g. Windows). It is part of the code imported from tmux. Update it to the SUS-standard uid_t. Closes #2821. --- src/env_universal_common.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/env_universal_common.cpp b/src/env_universal_common.cpp index a0b4ebfdc..71fb1667d 100644 --- a/src/env_universal_common.cpp +++ b/src/env_universal_common.cpp @@ -129,7 +129,7 @@ static int check_runtime_path(const char * path) */ struct stat statpath; - u_int uid = geteuid(); + uid_t uid = geteuid(); if (mkdir(path, S_IRWXU) != 0 && errno != EEXIST) return errno; From 9f0417b587ac6cec7343e287c12886adc459e01c Mon Sep 17 00:00:00 2001 From: Owen Richardson Date: Fri, 13 Nov 2015 06:06:35 +0000 Subject: [PATCH 016/363] make alt-L output respect multi-line prompts Fixes #718 --- .../functions/__fish_list_current_token.fish | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/share/functions/__fish_list_current_token.fish b/share/functions/__fish_list_current_token.fish index a88978cc3..0a0f2dab1 100644 --- a/share/functions/__fish_list_current_token.fish +++ b/share/functions/__fish_list_current_token.fish @@ -1,22 +1,26 @@ - -# -# This function is bound to Alt-L, it is used to list the contents of -# the directory under the cursor -# +# This function is typically bound to Alt-L, it is used to list the contents +# of the directory under the cursor. function __fish_list_current_token -d "List contents of token under the cursor if it is a directory, otherwise list the contents of the current directory" - set val (eval echo (commandline -t)) - printf "\n" - if test -d $val - ls $val - else - set dir (dirname $val) - if test $dir != . -a -d $dir - ls $dir - else - ls - end - end - commandline -f repaint -end + set val (eval echo (commandline -t)) + printf "\n" + if test -d $val + ls $val + else + set dir (dirname $val) + if test $dir != . -a -d $dir + ls $dir + else + ls + end + end + set -l line_count (count (fish_prompt)) + if test $line_count -gt 1 + for x in (seq 2 $line_count) + printf "\n" + end + end + + commandline -f repaint +end From de0349399c5fcf0aa624e6607c17fd47fd5301bf Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 20 Mar 2016 12:17:00 +0100 Subject: [PATCH 017/363] robbyrussell prompt: Check for git/hg existence Fixes #2826 --- share/tools/web_config/sample_prompts/robbyrussell.fish | 2 ++ 1 file changed, 2 insertions(+) diff --git a/share/tools/web_config/sample_prompts/robbyrussell.fish b/share/tools/web_config/sample_prompts/robbyrussell.fish index 1c7fa748b..0a895ba4b 100644 --- a/share/tools/web_config/sample_prompts/robbyrussell.fish +++ b/share/tools/web_config/sample_prompts/robbyrussell.fish @@ -14,6 +14,7 @@ function fish_prompt end function _is_git_repo + type -q git; or return 1 git status -s >/dev/null ^/dev/null end @@ -26,6 +27,7 @@ function fish_prompt end function _is_hg_repo + type -q hg; or return 1 hg summary >/dev/null ^/dev/null end From dedc7f6f03cbdea41a851779af83db19893c5558 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 20 Mar 2016 12:20:39 +0100 Subject: [PATCH 018/363] Fix acpi check in nim prompt --- share/tools/web_config/sample_prompts/nim.fish | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/share/tools/web_config/sample_prompts/nim.fish b/share/tools/web_config/sample_prompts/nim.fish index 87837bf86..347af1d4d 100644 --- a/share/tools/web_config/sample_prompts/nim.fish +++ b/share/tools/web_config/sample_prompts/nim.fish @@ -47,16 +47,7 @@ function fish_prompt set_color -o green echo -n ] - # Check if acpi exists - if not set -q __fish_nim_prompt_has_acpi - if type acpi > /dev/null - set -g __fish_nim_prompt_has_acpi '' - else - set -g __fish_nim_prompt_has_acpi '' # empty string - end - end - - if test "$__fish_nim_prompt_has_acpi" + if type -q acpi if [ (acpi -a 2> /dev/null | grep off) ] echo -n '─[' set_color -o red From 1828def8668dc95326a0f084e9c8370742656bc8 Mon Sep 17 00:00:00 2001 From: Cody Frazer Date: Sun, 20 Mar 2016 10:10:51 -0500 Subject: [PATCH 019/363] Add `--no-index` option completion for `git diff` --- share/completions/git.fish | 1 + 1 file changed, 1 insertion(+) diff --git a/share/completions/git.fish b/share/completions/git.fish index 5130ec34f..8ffdb071c 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -323,6 +323,7 @@ complete -f -c git -n '__fish_git_using_command commit' -a '(__fish_git_modified complete -c git -n '__fish_git_needs_command' -a diff -d 'Show changes between commits, commit and working tree, etc' complete -c git -n '__fish_git_using_command diff' -a '(__fish_git_ranges)' -d 'Branch' complete -c git -n '__fish_git_using_command diff' -l cached -d 'Show diff of changes in the index' +complete -c git -n '__fish_git_using_command diff' -l no-index -d 'Compare two paths on the filesystem' # TODO options ### difftool From fb0921249f4584e68699e336be249a655b9c8ede Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sun, 20 Mar 2016 12:00:34 -0700 Subject: [PATCH 020/363] add \r equivalent binding Add a binding that was overlooked by commit d65c63322ef52443b372c3c49dbd3584596fed6b. Fixes #2834 --- share/functions/fish_default_key_bindings.fish | 1 + 1 file changed, 1 insertion(+) diff --git a/share/functions/fish_default_key_bindings.fish b/share/functions/fish_default_key_bindings.fish index cac095d3d..8d86c7eec 100644 --- a/share/functions/fish_default_key_bindings.fish +++ b/share/functions/fish_default_key_bindings.fish @@ -16,6 +16,7 @@ function fish_default_key_bindings -d "Default (Emacs-like) key bindings for fis bind $argv \t complete bind $argv \e\n "commandline -i \n" + bind $argv \e\r "commandline -i \n" bind $argv \e\[A up-or-search bind $argv \e\[B down-or-search From c2f1df1d4af0c7e633528cb4c8caa79ef04b0b5a Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Thu, 10 Mar 2016 18:17:39 -0800 Subject: [PATCH 021/363] fix handling of non-ASCII chars in C locale The relevant standards allow the mbtowc/mbrtowc functions to reject non-ASCII characters (i.e., chars with the high bit set) when the locale is C or POSIX. The BSD libraries (e.g., on OS X) don't do this but the GNU libraries (e.g., on Linux) do. Like most programs we need the C/POSIX locales to allow arbitrary bytes. So explicitly check if we're in a single-byte locale (which would also include ISO-8859 variants) and simply pass-thru the chars without encoding or decoding. Fixes #2802. --- src/builtin.cpp | 47 ++++++++++++++-------------- src/common.cpp | 71 +++++++++++++++++++++++++++++------------- src/env.cpp | 7 ++--- src/fallback.cpp | 72 ++++++++++++++++++++++--------------------- src/fallback.h | 20 ++---------- src/history.cpp | 19 +++++++----- src/input_common.cpp | 11 ++++--- src/output.cpp | 58 +++++++++++++++++----------------- src/wutil.cpp | 29 +++++++---------- src/wutil.h | 6 ++-- tests/c-locale.err | 0 tests/c-locale.in | 35 +++++++++++++++++++++ tests/c-locale.out | 4 +++ tests/c-locale.status | 1 + 14 files changed, 215 insertions(+), 165 deletions(-) create mode 100644 tests/c-locale.err create mode 100644 tests/c-locale.in create mode 100644 tests/c-locale.out create mode 100644 tests/c-locale.status diff --git a/src/builtin.cpp b/src/builtin.cpp index 4597b18e8..f4de72c73 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -1907,18 +1907,18 @@ static int builtin_echo(parser_t &parser, io_streams_t &streams, wchar_t **argv) return STATUS_BUILTIN_OK; } -/** The pwd builtin. We don't respect -P to resolve symbolic links because we try to always resolve them. */ +// 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, io_streams_t &streams, wchar_t **argv) { - wchar_t dir_path[4096]; - wchar_t *res = wgetcwd(dir_path, 4096); - if (res == NULL) + wcstring res = wgetcwd(); + if (res.empty()) { return STATUS_BUILTIN_ERROR; } else { - streams.out.append(dir_path); + streams.out.append(res); streams.out.push_back(L'\n'); return STATUS_BUILTIN_OK; } @@ -2699,9 +2699,8 @@ static int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) while (1) { - int finished=0; - - wchar_t res=0; + int finished = 0; + wchar_t res = 0; mbstate_t state = {}; while (!finished) @@ -2713,24 +2712,26 @@ static int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) break; } - size_t sz = mbrtowc(&res, &b, 1, &state); - - switch (sz) + if (MB_CUR_MAX == 1) // single-byte locale { - case (size_t)(-1): - memset(&state, '\0', sizeof(state)); - break; + res = (unsigned char)b; + finished = 1; + } + else { + size_t sz = mbrtowc(&res, &b, 1, &state); + switch (sz) + { + case (size_t)-1: + memset(&state, 0, sizeof(state)); + break; - case (size_t)(-2): - break; - case 0: - finished = 1; - break; - - default: - finished=1; - break; + case (size_t)-2: + break; + default: + finished = 1; + break; + } } } diff --git a/src/common.cpp b/src/common.cpp index 2aa76cc5c..a796baca1 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -103,8 +103,7 @@ int fgetws2(wcstring *s, FILE *f) { errno=0; - c = getwc(f); - + c = fgetwc(f); if (errno == EILSEQ || errno == EINTR) { continue; @@ -148,8 +147,19 @@ static wcstring str2wcs_internal(const char *in, const size_t in_len) wcstring result; result.reserve(in_len); - mbstate_t state = {}; size_t in_pos = 0; + + if (MB_CUR_MAX == 1) // single-byte locale, all values are legal + { + while (in_pos < in_len) + { + result.push_back((unsigned char)in[in_pos]); + in_pos++; + } + return result; + } + + mbstate_t state = {}; while (in_pos < in_len) { wchar_t wc = 0; @@ -165,12 +175,12 @@ static wcstring str2wcs_internal(const char *in, const size_t in_len) { use_encode_direct = true; } - else if (ret == (size_t)(-2)) + else if (ret == (size_t)-2) { /* Incomplete sequence */ use_encode_direct = true; } - else if (ret == (size_t)(-1)) + else if (ret == (size_t)-1) { /* Invalid data */ use_encode_direct = true; @@ -266,9 +276,7 @@ std::string wcs2string(const wcstring &input) std::string result; result.reserve(input.size()); - mbstate_t state; - memset(&state, 0, sizeof(state)); - + mbstate_t state = {}; char converted[MB_LEN_MAX + 1]; for (size_t i=0; i < input.size(); i++) @@ -276,12 +284,22 @@ std::string wcs2string(const wcstring &input) wchar_t wc = input[i]; if (wc == INTERNAL_SEPARATOR) { + // Do nothing. } - else if ((wc >= ENCODE_DIRECT_BASE) && - (wc < ENCODE_DIRECT_BASE+256)) + else if (wc >= ENCODE_DIRECT_BASE && wc < ENCODE_DIRECT_BASE + 256) { result.push_back(wc - ENCODE_DIRECT_BASE); } + else if (MB_CUR_MAX == 1) // single-byte locale (C/POSIX/ISO-8859) + { + // If `wc` contains a wide character we emit a question-mark. + if (wc & ~0xFF) + { + wc = '?'; + } + converted[0] = wc; + result.append(converted, 1); + } else { memset(converted, 0, sizeof converted); @@ -311,38 +329,47 @@ std::string wcs2string(const wcstring &input) */ static 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; - CHECK(in, 0); CHECK(out, 0); - memset(&state, 0, sizeof(state)); + size_t in_pos = 0; + size_t out_pos = 0; + mbstate_t state = {}; while (in[in_pos]) { if (in[in_pos] == INTERNAL_SEPARATOR) { + // Do nothing. } - else if ((in[in_pos] >= ENCODE_DIRECT_BASE) && - (in[in_pos] < ENCODE_DIRECT_BASE+256)) + 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 if (MB_CUR_MAX == 1) // single-byte locale (C/POSIX/ISO-8859) + { + // If `wc` contains a wide character we emit a question-mark. + if (in[in_pos] & ~0xFF) + { + out[out_pos++] = '?'; + } + else + { + out[out_pos++] = (unsigned char)in[in_pos]; + } + } else { - res = wcrtomb(&out[out_pos], in[in_pos], &state); - - if (res == (size_t)(-1)) + size_t len = wcrtomb(&out[out_pos], in[in_pos], &state); + if (len == (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; + out_pos += len; } } in_pos++; diff --git a/src/env.cpp b/src/env.cpp index c24280bcc..56c9deb07 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -377,14 +377,13 @@ static void setup_path() int env_set_pwd() { - wchar_t dir_path[4096]; - wchar_t *res = wgetcwd(dir_path, 4096); - if (!res) + wcstring res = wgetcwd(); + if (res.empty()) { debug(0, _(L"Could not determine current working directory. Is your locale set correctly?")); return 0; } - env_set(L"PWD", dir_path, ENV_EXPORT | ENV_GLOBAL); + env_set(L"PWD", res.c_str(), ENV_EXPORT | ENV_GLOBAL); return 1; } diff --git a/src/fallback.cpp b/src/fallback.cpp index 7c5f84d23..ed29f5de9 100644 --- a/src/fallback.cpp +++ b/src/fallback.cpp @@ -606,7 +606,7 @@ static FILE *fw_data; static void fw_writer(wchar_t c) { - putwc(c, fw_data); + fputwc(c, fw_data); } /* @@ -648,33 +648,30 @@ int wprintf(const wchar_t *filter, ...) #endif #ifndef HAVE_FGETWC - wint_t fgetwc(FILE *stream) { - wchar_t res=0; - mbstate_t state; - memset(&state, '\0', sizeof(state)); + wchar_t res; + mbstate_t state = {}; while (1) { int b = fgetc(stream); - char bb; - - int sz; - if (b == EOF) + { return WEOF; + } - bb=b; - - sz = mbrtowc(&res, &bb, 1, &state); + if (MB_CUR_MAX == 1) // single-byte locale, all values are legal + { + return b; + } + char bb = b; + size_t sz = mbrtowc(&res, &bb, 1, &state); switch (sz) { case -1: - memset(&state, '\0', sizeof(state)); return WEOF; - case -2: break; case 0: @@ -683,35 +680,40 @@ wint_t fgetwc(FILE *stream) return res; } } - } - - -wint_t getwc(FILE *stream) -{ - return fgetwc(stream); -} - - #endif #ifndef HAVE_FPUTWC - 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 = 0; + mbstate_t state = {}; + char s[MB_CUR_MAX + 1] = {}; -wint_t putwc(wchar_t wc, FILE *stream) -{ - return fputwc(wc, stream); -} + if (MB_CUR_MAX == 1) // single-byte locale (C/POSIX/ISO-8859) + { + // If `wc` contains a wide character we emit a question-mark. + if (wc & ~0xFF) + { + wc = '?'; + } + s[0] = (char)wc; + res = fputs(s, stream); + } + else + { + size_t len = wcrtomb(s, wc, &state); + if (len == (size_t)-1) + { + debug(1, L"Wide character %d has no narrow representation", wc); + } + else { + res = fputs(s, stream); + } + } + return res == EOF ? WEOF : wc; +} #endif #ifndef HAVE_WCSTOK diff --git a/src/fallback.h b/src/fallback.h index e30444497..f3252c149 100644 --- a/src/fallback.h +++ b/src/fallback.h @@ -158,29 +158,13 @@ int vswprintf(wchar_t *out, size_t n, const wchar_t *filter, va_list va); #endif #ifndef HAVE_FGETWC -/** - Fallback implementation of fgetwc -*/ +// Fallback implementation of fgetwc. wint_t fgetwc(FILE *stream); - -/** - Fallback implementation of getwc -*/ -wint_t getwc(FILE *stream); - #endif #ifndef HAVE_FPUTWC - -/** - Fallback implementation of fputwc -*/ +// Fallback implementation of fputwc. wint_t fputwc(wchar_t wc, FILE *stream); -/** - Fallback implementation of putwc -*/ -wint_t putwc(wchar_t wc, FILE *stream); - #endif #ifndef HAVE_WCSTOK diff --git a/src/history.cpp b/src/history.cpp index 75e426387..a37d2b776 100644 --- a/src/history.cpp +++ b/src/history.cpp @@ -926,10 +926,9 @@ 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; - - bool was_backslash = 0; + const char *pos = begin; wcstring out; + bool was_backslash = false; bool first_char = true; bool timestamp_mode = false; time_t timestamp = 0; @@ -937,12 +936,18 @@ history_item_t history_t::decode_item_fish_1_x(const char *begin, size_t length) while (1) { wchar_t c; - mbstate_t state; size_t res; + mbstate_t state = {}; - memset(&state, 0, sizeof(state)); - - res = mbrtowc(&c, pos, end-pos, &state); + if (MB_CUR_MAX == 1) // single-byte locale + { + c = (unsigned char)*pos; + res = 1; + } + else + { + res = mbrtowc(&c, pos, end - pos, &state); + } if (res == (size_t)-1) { diff --git a/src/input_common.cpp b/src/input_common.cpp index ebe95fced..7c018c2d1 100644 --- a/src/input_common.cpp +++ b/src/input_common.cpp @@ -263,16 +263,17 @@ wchar_t input_common_readch(int timed) while (1) { wint_t b = readb(); - char bb; - size_t sz; + if (MB_CUR_MAX == 1) // single-byte locale, all values are legal + { + return (unsigned char)b; + } if ((b >= R_NULL) && (b < R_NULL + 1000)) return b; - bb=b; - - sz = mbrtowc(&res, &bb, 1, &state); + char bb = b; + size_t sz = mbrtowc(&res, &bb, 1, &state); switch (sz) { diff --git a/src/output.cpp b/src/output.cpp index 1707f4fb7..dbd97eb4b 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -386,32 +386,35 @@ int writeb(tputs_arg_t b) int writech(wint_t ch) { - mbstate_t state; - size_t i; char buff[MB_LEN_MAX+1]; - size_t bytes; + size_t len; - if ((ch >= ENCODE_DIRECT_BASE) && - (ch < ENCODE_DIRECT_BASE+256)) + if (ch >= ENCODE_DIRECT_BASE && ch < ENCODE_DIRECT_BASE + 256) { buff[0] = ch - ENCODE_DIRECT_BASE; - bytes=1; + len = 1; + } + else if (MB_CUR_MAX == 1) // single-byte locale (C/POSIX/ISO-8859) + { + // If `wc` contains a wide character we emit a question-mark. + if (ch & ~0xFF) + { + ch = '?'; + } + buff[0] = ch; + len = 1; } else { - memset(&state, 0, sizeof(state)); - bytes= wcrtomb(buff, ch, &state); - - switch (bytes) + mbstate_t state = {}; + len = wcrtomb(buff, ch, &state); + if (len == (size_t)-1) { - case (size_t)(-1): - { - return 1; - } + return 1; } } - for (i=0; i Date: Sun, 20 Mar 2016 04:02:42 +0100 Subject: [PATCH 022/363] fix w, e (with a trick to cope with big-words) --- share/functions/fish_vi_key_bindings.fish | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/share/functions/fish_vi_key_bindings.fish b/share/functions/fish_vi_key_bindings.fish index 5debe6166..10551832f 100644 --- a/share/functions/fish_vi_key_bindings.fish +++ b/share/functions/fish_vi_key_bindings.fish @@ -79,10 +79,10 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' bind B backward-bigword bind ge backward-word bind gE backward-bigword - bind w forward-word - bind W forward-bigword - bind e forward-word - bind E forward-bigword + bind w forward-word forward-char + bind W forward-bigword forward-char + bind e forward-char forward-word backward-char + bind E forward-bigword backward-char bind x delete-char bind X backward-delete-char From 168a156e5896fcaaf82a1b36587cde948f1765be Mon Sep 17 00:00:00 2001 From: Federico Ferri Date: Sun, 20 Mar 2016 02:08:23 +0100 Subject: [PATCH 023/363] implement swap-selection-start-stop function The swap-selection-start-stop function goes to the other end of the highlighted text, the equivalent of `o' for vim visual mode. Add binding to the swap-selection-start-stop function, `o' when in visual mode. Document swap-selection-start-stop, begin-selection, end-selection, kill-selection. --- doc_src/bind.txt | 8 ++++++++ share/functions/fish_vi_key_bindings.fish | 1 + src/input.cpp | 2 ++ src/input_common.h | 1 + src/reader.cpp | 11 +++++++++++ 5 files changed, 23 insertions(+) diff --git a/doc_src/bind.txt b/doc_src/bind.txt index 813026c3b..9b1f34a09 100644 --- a/doc_src/bind.txt +++ b/doc_src/bind.txt @@ -75,6 +75,8 @@ The following special input functions are available: - `beginning-of-line`, move to the beginning of the line +- `begin-selection`, start selecting text + - `capitalize-word`, make the current word begin with a capital letter - `complete`, guess the remainder of the current token @@ -93,6 +95,8 @@ The following special input functions are available: - `end-of-line`, move to the end of the line +- `end-selection`, end selecting text + - `explain`, print a description of possible problems with the current command - `forward-bigword`, move one whitespace-delimited word to the right @@ -109,12 +113,16 @@ The following special input functions are available: - `kill-line`, move everything from the cursor to the end of the line to the killring +- `kill-selection`, move the selected text to the killring + - `kill-whole-line`, move the line to the killring - `kill-word`, move the next word to the killring - `suppress-autosuggestion`, remove the current autosuggestion +- `swap-selection-start-stop`, go to the other end of the highlighted text without changing the selection + - `transpose-chars`, transpose two characters to the left of the cursor - `transpose-words`, transpose two words to the left of the cursor diff --git a/share/functions/fish_vi_key_bindings.fish b/share/functions/fish_vi_key_bindings.fish index 10551832f..de0f8bd6e 100644 --- a/share/functions/fish_vi_key_bindings.fish +++ b/share/functions/fish_vi_key_bindings.fish @@ -200,6 +200,7 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' bind -M visual W forward-bigword bind -M visual e forward-word bind -M visual E forward-bigword + bind -M visual o swap-selection-start-stop force-repaint for key in $eol_keys bind -M visual $key end-of-line diff --git a/src/input.cpp b/src/input.cpp index c3e26815b..b4277724b 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -132,6 +132,7 @@ static const wchar_t * const name_arr[] = L"suppress-autosuggestion", L"accept-autosuggestion", L"begin-selection", + L"swap-selection-start-stop", L"end-selection", L"kill-selection", L"forward-jump", @@ -242,6 +243,7 @@ static const wchar_t code_arr[] = R_SUPPRESS_AUTOSUGGESTION, R_ACCEPT_AUTOSUGGESTION, R_BEGIN_SELECTION, + R_SWAP_SELECTION_START_STOP, R_END_SELECTION, R_KILL_SELECTION, R_FORWARD_JUMP, diff --git a/src/input_common.h b/src/input_common.h index e605d18b7..8f4cdd52a 100644 --- a/src/input_common.h +++ b/src/input_common.h @@ -68,6 +68,7 @@ enum R_SUPPRESS_AUTOSUGGESTION, R_ACCEPT_AUTOSUGGESTION, R_BEGIN_SELECTION, + R_SWAP_SELECTION_START_STOP, R_END_SELECTION, R_KILL_SELECTION, R_FORWARD_JUMP, diff --git a/src/reader.cpp b/src/reader.cpp index a7e8ec7e5..50c8c93d4 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -4026,6 +4026,17 @@ const wchar_t *reader_readline(int nchars) break; } + case R_SWAP_SELECTION_START_STOP: + { + if (!data->sel_active) break; + size_t tmp = data->sel_begin_pos; + data->sel_begin_pos = data->command_line.position; + data->sel_start_pos = data->command_line.position; + editable_line_t *el = data->active_edit_line(); + update_buff_pos(el, tmp); + break; + } + case R_END_SELECTION: { data->sel_active = false; From 2e0205a7462c812559952c069f21aec58515785a Mon Sep 17 00:00:00 2001 From: Nyanpasu Date: Tue, 22 Mar 2016 06:52:11 +0800 Subject: [PATCH 024/363] Add missing "Universal Variables" to tutorial.hdr --- doc_src/tutorial.hdr | 1 + 1 file changed, 1 insertion(+) diff --git a/doc_src/tutorial.hdr b/doc_src/tutorial.hdr index 8877b0b4f..cb8427d4b 100644 --- a/doc_src/tutorial.hdr +++ b/doc_src/tutorial.hdr @@ -27,6 +27,7 @@ - $PATH - Startup - Autoloading Functions +- Universal Variables - Ready for more? \htmlonly[block] From a81bd697a8237ada71a24ebbf58fcf1b127e8f34 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 22 Mar 2016 11:09:36 +0100 Subject: [PATCH 025/363] Make reading ssh files case-insensitive Fixes #2843 --- share/functions/__fish_print_hostnames.fish | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/share/functions/__fish_print_hostnames.fish b/share/functions/__fish_print_hostnames.fish index 492954a49..385157768 100644 --- a/share/functions/__fish_print_hostnames.fish +++ b/share/functions/__fish_print_hostnames.fish @@ -26,9 +26,9 @@ function __fish_print_hostnames -d "Print a list of known hostnames" if test -r $file # Print hosts from system wide ssh configuration file # Note the non-capturing group to avoid printing "name" - string match -r '\s*Host(?:name)? \w.*' < $file | string replace -r '^\s*Host(?:name)?\s*(\S+)' '$1' - set known_hosts $known_hosts (string match -r '^\s*UserKnownHostsFile|^\s*GlobalKnownHostsFile' <$file \ - | string replace -r '.*KnownHostsFile\s*' '') + string match -ri '\s*Host(?:name)? \w.*' < $file | string replace -ri '^\s*Host(?:name)?\s*(\S+)' '$1' + set known_hosts $known_hosts (string match -ri '^\s*UserKnownHostsFile|^\s*GlobalKnownHostsFile' <$file \ + | string replace -ri '.*KnownHostsFile\s*' '') end end for file in $known_hosts From 2a4a539d8696bcd61bac5d558795c2eaa3f78c70 Mon Sep 17 00:00:00 2001 From: Andreas Nordal Date: Tue, 15 Mar 2016 22:03:27 +0100 Subject: [PATCH 026/363] Fix memory leak, error message when failing to open input file The early return skipped all cleanup. This problem is a case for the classic "goto fail" paradigm, but this change instead makes a few adjustments to take advantage of a previously unused level of indentation to conditionally execute the success path. The error message now prints the filename instead of "open", which should be more idiomatic. Tip: This patch makes sense if viewed with `git show --ignore-space-change`. --- src/fish.cpp | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/fish.cpp b/src/fish.cpp index 048c9cbe3..bb898be5f 100644 --- a/src/fish.cpp +++ b/src/fish.cpp @@ -550,34 +550,30 @@ int main(int argc, char **argv) } reader_exit(0, 0); } + else if (my_optind == argc) + { + // Interactive mode + check_running_fishd(); + res = reader_read(STDIN_FILENO, empty_ios); + } else { - if (my_optind == argc) + char *file = *(argv+(my_optind++)); + int fd = open(file, O_RDONLY); + if (fd == -1) { - // Interactive mode - check_running_fishd(); - res = reader_read(STDIN_FILENO, empty_ios); + perror(file); } else { - char **ptr; - char *file = *(argv+(my_optind++)); - int i; - int fd; - - - 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)) { wcstring sb; + char **ptr; + int i; for (i=1,ptr = argv+my_optind; *ptr; i++, ptr++) { if (i != 1) From de1258e09b3cd4605995297d400ff134fd68b226 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Wed, 23 Mar 2016 11:42:17 -0700 Subject: [PATCH 027/363] `fish --version` should write to stdout When explicitly asking for the fish version string the information should go to stdout rather than stderr. Also, there is no reason to use exit_without_destructors() rather than exit() in that code path. We actually want the side-effects of exit() such as flushing stdout and there aren't any threads or other things that could cause a normal exit to fail when that function is run. --- src/fish.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/fish.cpp b/src/fish.cpp index bb898be5f..17cd1f16a 100644 --- a/src/fish.cpp +++ b/src/fish.cpp @@ -382,7 +382,7 @@ static int fish_parse_opt(int argc, char **argv, std::vector *cmds) case 0: { fwprintf(stderr, _(L"getopt_long() unexpectedly returned zero\n")); - exit_without_destructors(127); + exit(127); } case 'c': @@ -405,8 +405,9 @@ static int fish_parse_opt(int argc, char **argv, std::vector *cmds) } else { - debug(0, _(L"Invalid value '%s' for debug level switch"), optarg); - exit_without_destructors(1); + fwprintf(stderr, _(L"Invalid value '%s' for debug level switch"), + optarg); + exit(1); } break; } @@ -444,15 +445,15 @@ static int fish_parse_opt(int argc, char **argv, std::vector *cmds) case 'v': { - fwprintf(stderr, _(L"%s, version %s\n"), PACKAGE_NAME, + fwprintf(stdout, _(L"%s, version %s\n"), PACKAGE_NAME, get_fish_version()); - exit_without_destructors(0); + exit(0); } default: { // We assume getopt_long() has already emitted a diagnostic msg. - exit_without_destructors(1); + exit(1); } } From 9d2b53450ac038bcef7c62191770e9b85b6017f9 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Mon, 21 Mar 2016 16:51:39 -0700 Subject: [PATCH 028/363] limit size of cd history to 25 directories The existing implementation grows the $dirprev array without bounds. Besides causing what would appear to be a memory leak it also makes the nextd and prevd commands more expensive than they need to be. It also makes it harder to create a useful "menu" cd command. In addition to implementing a reasonable limit on the size of the $dirprev array I've reformatted the code using fish_indent. Update the documentation to include mentions of the $dirprev and $dirnext variables as well as the limit on how much directory history is kept. Fixes 2836 --- doc_src/cd.txt | 2 +- doc_src/dirh.txt | 2 ++ doc_src/nextd.txt | 1 + doc_src/prevd.txt | 1 + share/functions/cd.fish | 57 ++++++++++++++++++++++------------------- 5 files changed, 36 insertions(+), 27 deletions(-) diff --git a/doc_src/cd.txt b/doc_src/cd.txt index 3153c383d..a2cb95885 100644 --- a/doc_src/cd.txt +++ b/doc_src/cd.txt @@ -14,7 +14,7 @@ If `DIRECTORY` is a relative path, the paths found in the `CDPATH` environment v Note that the shell will attempt to change directory without requiring `cd` if the name of a directory is provided (starting with `.`, `/` or `~`, or ending with `/`). -Fish also ships a wrapper function around `cd` that understands `cd -` as changing to the previous directory. See `prevd`. +Fish also ships a wrapper function around the builtin `cd` that understands `cd -` as changing to the previous directory. See also `prevd`. This wrapper function maintains a history of the 25 most recently visited directories in the `$dirprev` and `$dirnext` global variables. \subsection cd-example Examples diff --git a/doc_src/dirh.txt b/doc_src/dirh.txt index d25b9c2ad..77e03e185 100644 --- a/doc_src/dirh.txt +++ b/doc_src/dirh.txt @@ -10,3 +10,5 @@ dirh `dirh` prints the current directory history. The current position in the history is highlighted using the color defined in the `fish_color_history_current` environment variable. `dirh` does not accept any parameters. + +Note that the `cd` command limits directory history to the 25 most recently visited directories. The history is stored in the `$dirprev` and `$dirnext` variables. diff --git a/doc_src/nextd.txt b/doc_src/nextd.txt index 8d7c90350..e02598a9b 100644 --- a/doc_src/nextd.txt +++ b/doc_src/nextd.txt @@ -11,6 +11,7 @@ nextd [ -l | --list ] [POS] If the `-l` or `--list` flag is specified, the current directory history is also displayed. +Note that the `cd` command limits directory history to the 25 most recently visited directories. The history is stored in the `$dirprev` and `$dirnext` variables which this command manipulates. \subsection nextd-example Example diff --git a/doc_src/prevd.txt b/doc_src/prevd.txt index cd0c99ac4..f1e9e6cb3 100644 --- a/doc_src/prevd.txt +++ b/doc_src/prevd.txt @@ -11,6 +11,7 @@ prevd [ -l | --list ] [POS] If the `-l` or `--list` flag is specified, the current history is also displayed. +Note that the `cd` command limits directory history to the 25 most recently visited directories. The history is stored in the `$dirprev` and `$dirnext` variables which this command manipulates. \subsection prevd-example Example diff --git a/share/functions/cd.fish b/share/functions/cd.fish index 8faa469d7..a1b6b2ac9 100644 --- a/share/functions/cd.fish +++ b/share/functions/cd.fish @@ -1,36 +1,41 @@ # -# The following functions add support for a directory history +# Wrap the builtin cd command to maintain directory history. # - function cd --description "Change directory" + set -l MAX_DIR_HIST 25 - # Skip history in subshells - if status --is-command-substitution - builtin cd $argv - return $status - end + if test (count $argv) -gt 1 + printf "%s\n" (_ "Too many args for cd command") + return 1 + end - # Avoid set completions - set -l previous $PWD + # Skip history in subshells. + if status --is-command-substitution + builtin cd $argv + return $status + end - if test $argv[1] = - ^/dev/null - if test "$__fish_cd_direction" = next ^/dev/null - nextd - else - prevd - end - return $status - end + # Avoid set completions + set -l previous $PWD - builtin cd $argv[1] - set -l cd_status $status + if test "$argv" = "-" + if test "$__fish_cd_direction" = "next" + nextd + else + prevd + end + return $status + end - if test $cd_status = 0 -a "$PWD" != "$previous" - set -g dirprev $dirprev $previous - set -e dirnext - set -g __fish_cd_direction prev - end + builtin cd $argv + set -l cd_status $status - return $cd_status + if test $cd_status -eq 0 -a "$PWD" != "$previous" + set -q dirprev[$MAX_DIR_HIST]; and set -e dirprev[1] + set -g dirprev $dirprev $previous + set -e dirnext + set -g __fish_cd_direction prev + end + + return $cd_status end - From 516695ff21acb9160f403947840363dba5632f13 Mon Sep 17 00:00:00 2001 From: Bogdan Sinitsyn Date: Fri, 18 Mar 2016 15:43:59 +0300 Subject: [PATCH 029/363] add yaourt completions --- share/completions/yaourt.fish | 190 +++++++++++++++++++++++++++++++++- 1 file changed, 189 insertions(+), 1 deletion(-) diff --git a/share/completions/yaourt.fish b/share/completions/yaourt.fish index 9e19cca46..91a2401cd 100644 --- a/share/completions/yaourt.fish +++ b/share/completions/yaourt.fish @@ -1 +1,189 @@ -complete -c yaourt -w pacman +# Completions for yaourt + +set -l progname yaourt +complete -e -c $progname +complete -c $progname -f + +set -l listinstalled "(pacman -Q | string replace ' ' \t)" +# This might be an issue if another package manager is also installed (e.g. for containers) +set -l listall "(__fish_print_packages)" +set -l listrepos "(__fish_print_pacman_repos)" +set -l listgroups "(pacman -Sg)\t'Package Group'" + +set -l hasopt '__fish_contains_opt -s B backup -s C -s G getpkgbuild -s P pkgbuild -s D database -s Q query -s R remove -s S sync -s U upgrade -s F files stats' +set -l noopt "not $hasopt" + +set -l backup '__fish_contains_opt -s B backup' +set -l clean '__fish_contains_opt -s C' +set -l getpkgbuild '__fish_contains_opt -s G getpkgbuild' +set -l pkgbuild '__fish_contains_opt -s P pkgbuild' +set -l database '__fish_contains_opt -s D database' +set -l query '__fish_contains_opt -s Q query' +set -l remove '__fish_contains_opt -s R remove' +set -l sync '__fish_contains_opt -s S sync' +set -l upgrade '__fish_contains_opt -s U upgrade' +set -l files '__fish_contains_opt -s F files' + +# HACK: We only need these three to coerce fish to stop file completion and complete options +complete -c $progname -n $noopt -a "-D" -d "Modify the package database" +complete -c $progname -n $noopt -a "-Q" -d "Query the package database" +complete -c $progname -n $noopt -a "-C" -d "Manage .pac* files" +# Primary operations +complete -c $progname -s D -f -l database -n $noopt -d 'Modify the package database' +complete -c $progname -s Q -f -l query -n $noopt -d 'Query the package database' +complete -c $progname -s R -f -l remove -n $noopt -d 'Remove packages from the system' +complete -c $progname -s S -f -l sync -n $noopt -d 'Synchronize packages' +complete -c $progname -s T -f -l deptest -n $noopt -d 'Check dependencies' +complete -c $progname -s U -f -l upgrade -n $noopt -d 'Upgrade or add a local package' +complete -c $progname -s F -f -l files -n $noopt -d 'Query the files database' +complete -c $progname -s V -f -l version -d 'Display version and exit' +complete -c $progname -s h -f -l help -d 'Display help' +# $progname operations +complete -c $progname -n $noopt -s B -l backup -d "Backup or restore alpm local database" +complete -c $progname -n $noopt -s C -f -d "Manage .pac* files" +complete -c $progname -n $noopt -s G -f -l getpkgbuild -d "Get PKGBUILD from ABS or AUR" +complete -c $progname -n $noopt -s P -l pkgbuild -d "Build package from PKGBUILD found in a local directory" +complete -c $progname -n $noopt -f -l stats -d "Show some statistics about your packages" + +# General options +# Only offer these once a command has been given so they get prominent display +complete -c $progname -n $noopt -s b -l dbpath -d 'Alternative database location' -xa '(__fish_complete_directories)' +complete -c $progname -n $hasopt -s r -l root -d 'Alternative installation root' +complete -c $progname -n $hasopt -s v -l verbose -d 'Output more status messages' +complete -c $progname -n $hasopt -l arch -d 'Alternate architecture' -f +complete -c $progname -n $hasopt -l cachedir -d 'Alternative package cache location' +complete -c $progname -n $hasopt -l config -d 'Alternate config file' +complete -c $progname -n $hasopt -l debug -d 'Display debug messages' -f +complete -c $progname -n $hasopt -l gpgdir -d 'GPG directory to verify signatures' +complete -c $progname -n $hasopt -l hookdir -d 'Hook file directory' +complete -c $progname -n $hasopt -l logfile -d 'Specify alternative log file' +complete -c $progname -n $hasopt -l noconfirm -d 'Bypass any question' -f +# General options (yaourt only) +complete -c $progname -n $hasopt -l color -d 'Force color' +complete -c $progname -n $hasopt -l force -d 'Force installation or updates' +complete -c $progname -n $hasopt -l insecure -d 'Allow to perform "insecure" SSL connections' +complete -c $progname -n $hasopt -l nocolor -d 'Disable color' +complete -c $progname -n $hasopt -l confirm -d 'Always ask for confirmation' +complete -c $progname -n $hasopt -l pager -d 'Use $PAGER to show search results' +complete -c $progname -n $hasopt -l export -x -a '(__fish_complete_directories)' -d 'Export built packages and their sources to DIR' +complete -c $progname -n $hasopt -l tmp -x -a '(__fish_complete_directories)' -d 'Use DIR as temporary folder' + +# Transaction options (sync, remove, upgrade) +for condition in sync remove upgrade + complete -c $progname -n $$condition -s d -l nodeps -d 'Skip [all] dependency checks' -f + complete -c $progname -n $$condition -l dbonly -d 'Modify database entry only' -f + complete -c $progname -n $$condition -l noprogressbar -d 'Do not display progress bar' -f + complete -c $progname -n $$condition -l noscriptlet -d 'Do not execute install script' -f + complete -c $progname -n $$condition -s p -l print -d 'Dry run, only print targets' -f + complete -c $progname -n $$condition -l print-format -x -d 'Specify printf-like format' -f +end + +# Database and upgrade options (database, sync, upgrade) +for condition in database sync upgrade + complete -c $progname -n $$condition -l asdeps -d 'Mark PACKAGE as dependency' -f + complete -c $progname -n $$condition -l asexplicit -d 'Mark PACKAGE as explicitly installed' -f +end + +# Upgrade options (sync, upgrade) +for condition in sync upgrade + complete -c $progname -n $$condition -l force -d 'Bypass file conflict checks' -f + complete -c $progname -n $$condition -l ignore -d 'Ignore upgrade of PACKAGE' -xa "$listinstalled" -f + complete -c $progname -n $$condition -l ignoregroup -d 'Ignore upgrade of GROUP' -xa "$listgroups" -f + complete -c $progname -n $$condition -l needed -d 'Do not reinstall up-to-date targets' -f + complete -c $progname -n $$condition -l recursive -d 'Recursively reinstall all dependencies' -f +end + +# Query and sync options +for condition in query sync + complete -c $progname -n $$condition -s g -l groups -d 'Display all packages in GROUP' -xa "$listgroups" -f + complete -c $progname -n $$condition -s i -l info -d 'Display information on PACKAGE' -f + complete -c $progname -n $$condition -s q -l quiet -d 'Show less information' -f + complete -c $progname -n $$condition -s s -l search -r -d 'Search packages for regexp' -f + # Yaourt only + complete -c $progname -n $$condition -l conflicts -d 'Show packages that conflict with one of the targets' + complete -c $progname -n $$condition -l depends -d 'Show packages that depend on one of the targets' + complete -c $progname -n $$condition -l provides -d 'Show packages that provide one of the targets' + complete -c $progname -n $$condition -l replaces -d 'Show packages that replace one of the targets' + complete -c $progname -n $$condition -l nameonly -d 'Query the package names only' +end + +# Query options +complete -c $progname -n $query -s c -l changelog -d 'View the change log of PACKAGE' -f +complete -c $progname -n $query -s d -l deps -d 'List only non-explicit packages (dependencies)' -f +complete -c $progname -n $query -s e -l explicit -d 'List only explicitly installed packages' -f +complete -c $progname -n $query -s k -l check -d 'Check if all files owned by PACKAGE are present' -f +complete -c $progname -n $query -s l -l list -d 'List all files owned by PACKAGE' -f +complete -c $progname -n $query -s m -l foreign -d 'List all packages not in the database' -f +complete -c $progname -n $query -s o -l owns -r -d 'Search for the package that owns FILE' -xa '' -f +complete -c $progname -n $query -s p -l file -d 'Apply the query to a package file, not package' -xa '' -f +complete -c $progname -n $query -s t -l unrequired -d 'List only unrequired packages' -f +complete -c $progname -n $query -s u -l upgrades -d 'List only out-of-date packages' -f +complete -c $progname -n "$query" -d 'Installed package' -xa $listinstalled -f +# Yaourt only query options +# Backup file is always saved as pacman-$date.tar.bz2 +complete -c $progname -n $query -r -l backupfile -a '(__fish_complete_suffix tar.bz2)' -d 'Query FILE instead of alpm/aur' +complete -c $progname -n $query -l date -d 'List queries result sorted by installation date' + +# Remove options +complete -c $progname -n $remove -s c -l cascade -d 'Also remove packages depending on PACKAGE' -f +complete -c $progname -n $remove -s n -l nosave -d 'Ignore file backup designations' -f +complete -c $progname -n $remove -s s -l recursive -d 'Also remove dependencies of PACKAGE' -f +complete -c $progname -n $remove -s u -l unneeded -d 'Only remove targets not required by PACKAGE' -f +complete -c $progname -n "$remove" -d 'Installed package' -xa $listinstalled -f + +# Sync options +complete -c $progname -n $sync -s c -l clean -d 'Remove [all] packages from cache' +complete -c $progname -n $sync -s l -l list -xa "$listrepos" -d 'List all packages in REPOSITORY' +complete -c $progname -n "$sync; and not __fish_contains_opt -s u sysupgrade" -s u -l sysupgrade -d 'Upgrade all packages that are out of date' +complete -c $progname -n "$sync; and __fish_contains_opt -s u sysupgrade" -s u -l sysupgrade -d 'Also downgrade packages' +complete -c $progname -n $sync -s w -l downloadonly -d 'Only download the target packages' +complete -c $progname -n $sync -s y -l refresh -d 'Download fresh copy of the package list' +complete -c $progname -n "$sync" -xa "$listall $listgroups" +# Additional sync options +complete -c $progname -n $sync -s a -l aur -d 'Also search in AUR database' +complete -c $progname -n $sync -s A -l ignorearch -d 'Pass -A or --ignorearch option to makepkg' +complete -c $progname -n $sync -l aur-url -x -d 'Specify a custom AUR url' +set -l has_build_opt '__fish_contains_opt -s b build' +complete -c $progname -n "$sync; and not $has_build_opt" -s b -l build -d 'Build from sources(AUR or ABS)' +complete -c $progname -n "$sync; and $has_build_opt" -s b -l build -d 'Also build all dependencies' +complete -c $progname -n $sync -l devel -d 'Search an update for devel packages' +complete -c $progname -n $sync -l maintainer -d 'Search packages by maintainer instead of name (AUR only)' +complete -c $progname -n $sync -l m-arg -d 'Pass additional options to makepkg' + +# Database options +set -l has_db_opt '__fish_contains_opt asdeps asexplicit' +complete -c $progname -n "$database; and not $has_db_opt" -xa --asdeps -d 'Mark PACKAGE as dependency' +complete -c $progname -n "$database; and not $has_db_opt" -xa --asexplicit -d 'Mark PACKAGE as explicitly installed' +complete -c $progname -n "$database; and not $has_db_opt" -s k -l check -d 'Check database validity' +complete -c $progname -n "$has_db_opt; and $database" -xa "$listinstalled" + +# File options - since pacman 5 +set -l has_file_opt '__fish_contains_opt list search -s l -s s' +complete -c $progname -n "$files; and not $has_file_opt" -xa --list -d 'List files owned by given packages' +complete -c $progname -n "$files; and not $has_file_opt" -xa -l -d 'List files owned by given packages' +complete -c $progname -n "$files; and not $has_file_opt" -xa --search -d 'Search packages for matching files' +complete -c $progname -n "$files; and not $has_file_opt" -xa -s -d 'Search packages for matching files' +complete -c $progname -n "$files" -s y -l refresh -d 'Refresh the files database' -f +complete -c $progname -n "$files" -s l -l list -d 'List files owned by given packages' -xa $listall +complete -c $progname -n "$files" -s s -l search -d 'Search packages for matching files' +complete -c $progname -n "$files" -s o -l owns -d 'Search for packages that include the given files' +complete -c $progname -n "$files" -s q -l quiet -d 'Show less information' -f +complete -c $progname -n "$files" -l machinereadable -d 'Show in machine readable format' -f + +# Upgrade options +# Theoretically, pacman reads packages in all formats that libarchive supports +# In practice, it's going to be tar.xz or tar.gz +# Using "pkg.tar.*" here would change __fish_complete_suffix's descriptions to "unknown" +complete -c $progname -n "$upgrade" -xa '(__fish_complete_suffix pkg.tar.xz)' -d 'Package file' +complete -c $progname -n "$upgrade" -xa '(__fish_complete_suffix pkg.tar.gz)' -d 'Package file' + +## Yaourt only stuff +# Clean options +set -l has_clean_opt '__fish_contains_opt clean' +complete -c $progname -n "$clean; and not $has_clean_opt" -xa --clean -d 'Clean all these files' + +# pkgbuild options +complete -c $progname -n $pkgbuild -s i -l install -d 'Also install the package' + +# getpkgbuild options +complete -c $progname -n $getpkgbuild -xa "$listall" From ad97a122c9093610ed0807553e6592a658e5a7a7 Mon Sep 17 00:00:00 2001 From: CharlonTank Date: Thu, 24 Mar 2016 18:31:26 -0700 Subject: [PATCH 030/363] Update README.md Add a command that instantly add the fish path to /etc/shells --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index bfbd790ef..fc549ef61 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,10 @@ If you wish to use fish as your default shell, use the following command: chsh will prompt you for your password, and change your default shell. Substitute "/usr/local/bin/fish" with whatever path to fish is in your /etc/shells file. +Use the following command if you didn't already add your fish path to /etc/shells. + + echo /usr/local/bin/fish | sudo tee -a /etc/shells + To switch your default shell back, you can run: chsh -s /bin/bash From 7accadc33f817a3e17c14c989a3b83f0d6737665 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 26 Mar 2016 19:16:17 +0100 Subject: [PATCH 031/363] Only read .fish files in the snippets directories This would allow us to add a README and allows users to easily disable something temporarily. --- doc_src/index.hdr.in | 2 +- share/config.fish | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index ed497a992..ff2d5ab36 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -1088,7 +1088,7 @@ function on_exit --on-process %self end \endfish -Right after reading /usr/share/fish/config.fish and before reading /etc/fish/config.fish, fish will also read files in ~/.config/fish/conf.d/, /etc/fish/conf.d and /usr/share/fish/vendor_conf.d (the exact values depend on $XDG_CONFIG_HOME, $__fish_sysconfdir and $__fish_datadir). If there are files with the same name in two or all of these, fish will only attempt to read the first (skipping all files with that name if it is unreadable). ~/.config takes precedence over /etc/ which takes precedence over /usr. The path to the latter can also be gotten via `pkg-config` as "confdir", and is meant for third-party applications to integrate with fish. +Right after reading /usr/share/fish/config.fish and before reading /etc/fish/config.fish, fish will also read files ending in ".fish" in ~/.config/fish/conf.d/, /etc/fish/conf.d and /usr/share/fish/vendor_conf.d (the exact values depend on $XDG_CONFIG_HOME, $__fish_sysconfdir and $__fish_datadir). If there are files with the same name in two or all of these, fish will only attempt to read the first (skipping all files with that name if it is unreadable). ~/.config takes precedence over /etc/ which takes precedence over /usr. The path to the latter can also be gotten via `pkg-config` as "confdir", and is meant for third-party applications to integrate with fish. \section other Other features diff --git a/share/config.fish b/share/config.fish index 9ed3250a7..fe45f70d7 100644 --- a/share/config.fish +++ b/share/config.fish @@ -157,7 +157,7 @@ end # As last part of initialization, source the conf directories # Implement precedence (User > Admin > Vendors > Fish) by basically doing "basename" set -l sourcelist -for file in $configdir/fish/conf.d/* $__fish_sysconfdir/conf.d/* $__fish_datadir/vendor_conf.d/* +for file in $configdir/fish/conf.d/*.fish $__fish_sysconfdir/conf.d/*.fish $__fish_datadir/vendor_conf.d/*.fish set -l basename (string replace -r '^.*/' '' -- $file) contains -- $basename $sourcelist; and continue set sourcelist $sourcelist $basename From 6495bd470d40b2b489e6fe350e2da08be64d4bec Mon Sep 17 00:00:00 2001 From: Andreas Nordal Date: Fri, 18 Mar 2016 23:14:16 +0100 Subject: [PATCH 032/363] Fix memory leaks at exit found in tests This fixes all memory leaks found by compiling with clang++ -g -fsanitize=address and running the tests. Method: Ensure that memory is freed by the destructor of its respective container, either by storing objects directly instead of by pointer, or implementing the required destructor. --- src/complete.cpp | 92 ++++++++++++++++------------------------------ src/fish_tests.cpp | 4 +- src/history.cpp | 54 ++++++++++++++++++++++----- src/history.h | 13 +++---- src/wutil.cpp | 16 ++++---- 5 files changed, 95 insertions(+), 84 deletions(-) diff --git a/src/complete.cpp b/src/complete.cpp index 5e77af256..8af2c822f 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -186,7 +186,6 @@ public: /** Adds or removes an option. */ void add_option(const complete_entry_opt_t &opt); bool remove_option(const wcstring &option, complete_option_type_t type); - void remove_all_options(); completion_entry_t(const wcstring &c, bool type, bool author) : cmd(c), @@ -201,20 +200,20 @@ public: struct completion_entry_set_comparer { /** Comparison for std::set */ - bool operator()(completion_entry_t *p1, completion_entry_t *p2) const + bool operator()(const completion_entry_t &p1, const 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; + return p1.cmd_is_path < p2.cmd_is_path; } else { - return p1->cmd < p2->cmd; + return p1.cmd < p2.cmd; } } }; -typedef std::set completion_entry_set_t; +typedef std::set completion_entry_set_t; static completion_entry_set_t completion_set; // Comparison function to sort completions by their order field @@ -520,46 +519,28 @@ 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) -{ - ASSERT_IS_LOCKED(completion_lock); - completion_entry_t *result = NULL; - completion_entry_t tmp_entry(cmd, cmd_is_path, false); - completion_entry_set_t::iterator iter = completion_set.find(&tmp_entry); - if (iter != completion_set.end()) - { - result = *iter; - } - 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; - c = complete_find_exact_entry(cmd, cmd_is_path); + std::pair ins = + completion_set.insert(completion_entry_t(cmd, cmd_is_path, false)); - if (c == NULL) - { - c = new completion_entry_t(cmd, cmd_is_path, false); - completion_set.insert(c); - } - - return c; + // NOTE SET_ELEMENTS_ARE_IMMUTABLE: Exposing mutable access here is only + // okay as long as callers do not change any field that matters to ordering + // - affecting order without telling std::set invalidates its internal state + return const_cast(*ins.first); } void complete_set_authoritative(const wchar_t *cmd, bool cmd_is_path, bool authoritative) { - completion_entry_t *c; - CHECK(cmd,); scoped_lock lock(completion_lock); - c = complete_get_exact_entry(cmd, cmd_is_path); - c->authoritative = authoritative; + + completion_entry_t &c = complete_get_exact_entry(cmd, cmd_is_path); + c.authoritative = authoritative; } @@ -580,8 +561,7 @@ void complete_add(const wchar_t *cmd, /* Lock the lock that allows us to edit the completion entry list */ scoped_lock lock(completion_lock); - completion_entry_t *c; - c = complete_get_exact_entry(cmd, cmd_is_path); + completion_entry_t &c = complete_get_exact_entry(cmd, cmd_is_path); /* Create our new option */ complete_entry_opt_t opt; @@ -594,7 +574,7 @@ void complete_add(const wchar_t *cmd, if (desc) opt.desc = desc; opt.flags = flags; - c->add_option(opt); + c.add_option(opt); } /** @@ -621,28 +601,23 @@ bool completion_entry_t::remove_option(const wcstring &option, complete_option_t return this->options.empty(); } -void completion_entry_t::remove_all_options() -{ - ASSERT_IS_LOCKED(completion_lock); - this->options.clear(); -} - void complete_remove(const wcstring &cmd, bool cmd_is_path, const wcstring &option, complete_option_type_t type) { scoped_lock lock(completion_lock); completion_entry_t tmp_entry(cmd, cmd_is_path, false); - completion_entry_set_t::iterator iter = completion_set.find(&tmp_entry); + completion_entry_set_t::iterator iter = completion_set.find(tmp_entry); if (iter != completion_set.end()) { - completion_entry_t *entry = *iter; - bool delete_it = entry->remove_option(option, type); + // const_cast: See SET_ELEMENTS_ARE_IMMUTABLE + completion_entry_t &entry = const_cast(*iter); + + bool delete_it = entry.remove_option(option, type); if (delete_it) { /* Delete this entry */ completion_set.erase(iter); - delete entry; } } } @@ -652,14 +627,7 @@ void complete_remove_all(const wcstring &cmd, bool cmd_is_path) scoped_lock lock(completion_lock); completion_entry_t tmp_entry(cmd, cmd_is_path, false); - completion_entry_set_t::iterator iter = completion_set.find(&tmp_entry); - if (iter != completion_set.end()) - { - completion_entry_t *entry = *iter; - entry->remove_all_options(); - completion_set.erase(iter); - delete entry; - } + completion_set.erase(tmp_entry); } @@ -1187,12 +1155,12 @@ bool completer_t::complete_param(const wcstring &scmd_orig, const wcstring &spop scoped_lock lock(completion_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)) + const completion_entry_t &i = *iter; + const wcstring &match = i.cmd_is_path ? path : cmd; + if (wildcard_match(match, i.cmd)) { /* Copy all of their options into our list */ - all_options.push_back(i->get_options()); //Oof, this is a lot of copying + all_options.push_back(i.get_options()); //Oof, this is a lot of copying } } } @@ -1909,7 +1877,11 @@ wcstring complete_print() scoped_lock locker(completion_lock); // Get a list of all completions in a vector, then sort it by order - std::vector all_completions(completion_set.begin(), completion_set.end()); + std::vector all_completions; + for (completion_entry_set_t::const_iterator i = completion_set.begin(); i != completion_set.end(); ++i) + { + all_completions.push_back(&*i); + } sort(all_completions.begin(), all_completions.end(), compare_completions_by_order); for (std::vector::const_iterator iter = all_completions.begin(); iter != all_completions.end(); ++iter) diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index dfc346092..5bacab80e 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -2645,9 +2645,11 @@ static void test_universal() if (system("mkdir -p /tmp/fish_uvars_test/")) err(L"mkdir failed"); const int threads = 16; + static int ctx[threads]; for (int i=0; i < threads; i++) { - iothread_perform(test_universal_helper, new int(i)); + ctx[i] = i; + iothread_perform(test_universal_helper, &ctx[i]); } iothread_drain_all(); diff --git a/src/history.cpp b/src/history.cpp index a37d2b776..ff99db9a0 100644 --- a/src/history.cpp +++ b/src/history.cpp @@ -56,6 +56,9 @@ Our history format is intended to be valid YAML. Here it is: /** Default buffer size for flushing to the history file */ #define HISTORY_OUTPUT_BUFFER_SIZE (4096 * 4) +namespace +{ + /* Helper class for certain output. This is basically a string that allows us to ensure we only flush at record boundaries, and avoids the copying of ostringstream. Have you ever tried to implement your own streambuf? Total insanity. */ class history_output_buffer_t { @@ -168,7 +171,7 @@ public: explicit history_lru_node_t(const history_item_t &item) : lru_node_t(item.str()), timestamp(item.timestamp()), - required_paths(item.required_paths) + required_paths(item.get_required_paths()) {} }; @@ -207,9 +210,31 @@ public: } }; -static pthread_mutex_t hist_lock = PTHREAD_MUTEX_INITIALIZER; +class history_collection_t +{ + pthread_mutex_t m_lock; + std::map m_histories; -static std::map histories; +public: + history_collection_t() + { + VOMIT_ON_FAILURE_NO_ERRNO(pthread_mutex_init(&m_lock, NULL)); + } + ~history_collection_t() + { + for (std::map::const_iterator i = m_histories.begin(); i != m_histories.end(); ++i) + { + delete i->second; + } + pthread_mutex_destroy(&m_lock); + } + history_t& alloc(const wcstring &name); + void save(); +}; + +} //anonymous namespace + +static history_collection_t histories; static wcstring history_filename(const wcstring &name, const wcstring &suffix); @@ -524,16 +549,21 @@ static size_t offset_of_next_item(const char *begin, size_t mmap_length, history return result; } -history_t & history_t::history_with_name(const wcstring &name) +history_t & history_collection_t::alloc(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]; + scoped_lock locker(m_lock); + history_t *& current = m_histories[name]; if (current == NULL) current = new history_t(name); return *current; } +history_t & history_t::history_with_name(const wcstring &name) +{ + return histories.alloc(name); +} + history_t::history_t(const wcstring &pname) : name(pname), first_unwritten_new_item_index(0), @@ -1809,15 +1839,21 @@ void history_init() } -void history_destroy() +void history_collection_t::save() { /* Save all histories */ - for (std::map::iterator iter = histories.begin(); iter != histories.end(); ++iter) + for (std::map::iterator iter = m_histories.begin(); iter != m_histories.end(); ++iter) { - iter->second->save(); + history_t *hist = iter->second; + hist->save(); } } +void history_destroy() +{ + histories.save(); +} + void history_sanity_check() { diff --git a/src/history.h b/src/history.h index f99950aa0..58291f22a 100644 --- a/src/history.h +++ b/src/history.h @@ -43,7 +43,6 @@ typedef uint32_t history_identifier_t; class history_item_t { friend class history_t; - friend class history_lru_node_t; friend class history_tests_t; private: @@ -115,15 +114,9 @@ class history_t history_t(const history_t&); history_t &operator=(const history_t&); - /** Private creator */ - explicit history_t(const wcstring &pname); - /** Privately add an item. If pending, the item will not be returned by history searches until a call to resolve_pending. */ void add(const history_item_t &item, bool pending = false); - /** Destructor */ - ~history_t(); - /** Lock for thread safety */ pthread_mutex_t lock; @@ -208,6 +201,12 @@ class history_t static history_item_t decode_item(const char *base, size_t len, history_file_type_t type); public: + /** Constructor */ + explicit history_t(const wcstring &); + + /** Destructor */ + ~history_t(); + /** Returns history with the given name, creating it if necessary */ static history_t & history_with_name(const wcstring &name); diff --git a/src/wutil.cpp b/src/wutil.cpp index 2ff358bc8..62602c2cf 100644 --- a/src/wutil.cpp +++ b/src/wutil.cpp @@ -44,8 +44,8 @@ const file_id_t kInvalidFileID = {(dev_t)-1LL, (ino_t)-1LL, (uint64_t)-1LL, -1, /* Lock to protect wgettext */ static pthread_mutex_t wgettext_lock; -/* Maps string keys to (immortal) pointers to string values. */ -typedef std::map wgettext_map_t; +/* Map used as cache by wgettext. */ +typedef std::map wgettext_map_t; static wgettext_map_t wgettext_map; bool wreaddir_resolving(DIR *dir, const std::wstring &dir_path, std::wstring &out_name, bool *out_is_dir) @@ -488,16 +488,18 @@ const wchar_t *wgettext(const wchar_t *in) wcstring key = in; scoped_lock lock(wgettext_lock); - // Reference to pointer to string - const wchar_t *& val = wgettext_map[key]; - if (val == NULL) + wcstring &val = wgettext_map[key]; + if (val.empty()) { cstring mbs_in = wcs2string(key); char *out = fish_gettext(mbs_in.c_str()); - val = wcsdup(format_string(L"%s", out).c_str()); //note that this writes into the map! + val = format_string(L"%s", out).c_str(); } errno = err; - return val; //looks dangerous but is safe, since the string is stored in the map + + // The returned string is stored in the map + // TODO: If we want to shrink the map, this would be a problem + return val.c_str(); } int wmkdir(const wcstring &name, int mode) From bb5608e920043070498a16bec30fb4d3cebfd0db Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 27 Mar 2016 18:01:19 -0700 Subject: [PATCH 033/363] Remove an unnecessary c_str() --- src/wutil.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wutil.cpp b/src/wutil.cpp index 62602c2cf..a4494470d 100644 --- a/src/wutil.cpp +++ b/src/wutil.cpp @@ -493,7 +493,7 @@ const wchar_t *wgettext(const wchar_t *in) { cstring mbs_in = wcs2string(key); char *out = fish_gettext(mbs_in.c_str()); - val = format_string(L"%s", out).c_str(); + val = format_string(L"%s", out); } errno = err; From 6663c73eb0f1eedf5290bf07398d6dbe30eaaf73 Mon Sep 17 00:00:00 2001 From: Josef Gajdusek Date: Sun, 27 Mar 2016 23:00:19 +0200 Subject: [PATCH 034/363] Fix typos: ingnore->ignore --- share/completions/modprobe.fish | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/share/completions/modprobe.fish b/share/completions/modprobe.fish index 540fa5a24..14133ad55 100644 --- a/share/completions/modprobe.fish +++ b/share/completions/modprobe.fish @@ -7,8 +7,8 @@ complete -c modprobe -s v -l verbose --description "Print messages about what th complete -c modprobe -s C -l config --description "Configuration file" -r complete -c modprobe -s c -l showconfig --description "Dump configuration file" complete -c modprobe -s n -l dry-run --description "Do not actually insert/remove module" -complete -c modprobe -s i -l ingnore-install --description "Ignore install and remove commands in configuration file" -complete -c modprobe -l ingnore-remove --description "Ignore install and remove commands in configuration file" +complete -c modprobe -s i -l ignore-install --description "Ignore install and remove commands in configuration file" +complete -c modprobe -l ignore-remove --description "Ignore install and remove commands in configuration file" complete -c modprobe -s q -l quiet --description "Ignore bogus module names" complete -c modprobe -s r -l remove --description "Remove modules" complete -c modprobe -s V -l version --description "Display version and exit" From 0eb3fd6b3f25e6fd0f22ed4327b2aa1b019cde18 Mon Sep 17 00:00:00 2001 From: Josef Gajdusek Date: Mon, 28 Mar 2016 00:39:15 +0200 Subject: [PATCH 035/363] Make modprobe completion more precise Only match loaded modules when -r is specified. Also adds /lib/modules/(uname -r)/misc to the search path. This directory is used by Gentoo for package-provided modules (such as the app-emulation/virtualbox-modules) --- share/completions/modprobe.fish | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/share/completions/modprobe.fish b/share/completions/modprobe.fish index 14133ad55..8075d4373 100644 --- a/share/completions/modprobe.fish +++ b/share/completions/modprobe.fish @@ -2,7 +2,8 @@ # Completions for the modprobe command # -complete -c modprobe --no-files -d Module -a "(find /lib/modules/(uname -r)/kernel -type f | sed -e 's/\/.*\/\([^\/.]*\).*/\1/')" +complete -c modprobe -n "__fish_contains_opt -s r remove" --no-files -d Module -a "(lsmod | cut -d' ' -f1)" +complete -c modprobe -n "not __fish_contains_opt -s r remove" --no-files -d Module -a "(find /lib/modules/(uname -r)/{kernel,misc} -type f 2>/dev/null | sed -e 's/\/.*\/\([^\/.]*\).*/\1/')" complete -c modprobe -s v -l verbose --description "Print messages about what the program is doing" complete -c modprobe -s C -l config --description "Configuration file" -r complete -c modprobe -s c -l showconfig --description "Dump configuration file" From 2eb518f59c2401185ab58ba2639fb8907e68e6d0 Mon Sep 17 00:00:00 2001 From: David Adam Date: Mon, 28 Mar 2016 20:56:56 +0800 Subject: [PATCH 036/363] travis: enable leak detection in address sanitiser Since #2849 was merged, there are no further leaks detected by the address sanitiser. This makes it a good target to enable for Travis, which will enable regression testing. Closes #2851. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 54c047db2..cc883e8c6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,7 +32,7 @@ matrix: env: - CXXFLAGS="-g -fno-omit-frame-pointer -fsanitize=address" - ASAN_SYMBOLIZER_PATH="/usr/bin/llvm-symbolizer-3.6" - - ASAN_OPTIONS=check_initialization_order=1:detect_stack_use_after_return=1:detect_leaks=0 + - ASAN_OPTIONS=check_initialization_order=1:detect_stack_use_after_return=1:detect_leaks=1 before_install: export CXX=clang++-3.6 - os: osx From daf94e14d404060537317c5d925e8732ff4f8f44 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 29 Mar 2016 15:24:58 +0200 Subject: [PATCH 037/363] Document more keybindings Fixes #2866. --- doc_src/index.hdr.in | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index ff2d5ab36..ca3075390 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -909,7 +909,7 @@ Similar to bash, fish has Emacs and Vi editing modes. The default editing mode i \subsection emacs-mode Emacs mode commands -- @key{Tab} completes the current token. +- @key{Tab} completes the current token. @key{Shift, Tab} completes the current token and starts the pager's search mode. - @key{Home} or @key{Control,A} moves the cursor to the beginning of the line. @@ -935,7 +935,7 @@ Similar to bash, fish has Emacs and Vi editing modes. The default editing mode i - @key{Control,L} clears and repaints the screen. -- @key{Control,W} moves the previous word to the killring. +- @key{Control,W} moves the previous path component (everything up to the previous "/") to the killring. - @key{Alt,D} moves the next word to the killring. @@ -951,6 +951,11 @@ Similar to bash, fish has Emacs and Vi editing modes. The default editing mode i - @key{Alt,H} (or @key{F1}) shows the manual page for the current command, if one exists. +- @key{Control, t} transposes the last two characters + +- @key{Alt,t} transposes the last two words + + You can change these key bindings using the bind builtin command. From 19dd28e4006d952d8f03cbc773c4bb75807f7e51 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 29 Mar 2016 15:54:20 +0200 Subject: [PATCH 038/363] Document pager search Fixes #2866. --- doc_src/index.hdr.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index ca3075390..023bf3f3c 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -304,7 +304,7 @@ Autosuggestions are a powerful way to quickly summon frequently entered commands \section completion Tab completion -Tab completion is one of the most time saving features of any modern shell. By tapping the tab key, the user asks `fish` to guess the rest of the command or parameter that the user is currently typing. If `fish` can only find one possible completion, `fish` will write it out. If there is more than one completion, `fish` will write out the longest prefix that all completions have in common. If the completions differ on the first character, a list of all possible completions is printed. The list features descriptions of the completions and if the list doesn't fit the screen, it is scrollable by using the arrow keys, the page up/page down keys, the tab key or the space bar. Pressing any other key will exit the list and insert the pressed key into the command line. +Tab completion is one of the most time saving features of any modern shell. By tapping the tab key, the user asks `fish` to guess the rest of the command or parameter that the user is currently typing. If `fish` can only find one possible completion, `fish` will write it out. If there is more than one completion, `fish` will write out the longest prefix that all completions have in common. If the completions differ on the first character, a list of all possible completions is printed. The list features descriptions of the completions and if the list doesn't fit the screen, it is scrollable by using the arrow keys, the page up/page down keys, the tab key or the space bar. Once the list has been entered, pressing any other key will start a search. If the list has not been entered, pressing any other key will exit the list and insert the pressed key into the command line. These are the general purpose tab completions that `fish` provides: From d30f8fffc89c0e9f7e69b6c72897c0549fbd2ac5 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 29 Mar 2016 15:55:02 +0200 Subject: [PATCH 039/363] Reword: Always call suggestions sugggestions Not completions. --- doc_src/index.hdr.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index 023bf3f3c..e4eabf48d 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -295,7 +295,7 @@ Help on a specific builtin can also be obtained with the `-h` parameter. For ins \section autosuggestions Autosuggestions -fish suggests commands as you type, based on command history, completions, and valid file paths. As you type commands, you will see a completion offered after the cursor, in a muted gray color (which can be changed with the `fish_color_autosuggestion` variable). +fish suggests commands as you type, based on command history, completions, and valid file paths. As you type commands, you will see a suggestion offered after the cursor, in a muted gray color (which can be changed with the `fish_color_autosuggestion` variable). To accept the autosuggestion (replacing the command line contents), press right arrow or @key{Control,F}. To accept the first suggested word, press @key{Alt,→,Right} or @key{Alt,F}. If the autosuggestion is not what you want, just ignore it: it won't execute unless you accept it. From dfb23c4fce11d9d0577a2cb3c9e192bc24f64c8c Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Tue, 29 Mar 2016 15:33:37 -0700 Subject: [PATCH 040/363] fix regression to vi-mode \cc binding Commit c0e8ad6 on 2015-10-02 to "Make vi bindings inherit the defaults" inadvertently reverted commit b6b6de3. Fix that regression. And while I hate to make "git blame" say I changed the entire file make the function adhere to fish_indent style. --- share/functions/fish_vi_key_bindings.fish | 377 +++++++++++----------- 1 file changed, 189 insertions(+), 188 deletions(-) diff --git a/share/functions/fish_vi_key_bindings.fish b/share/functions/fish_vi_key_bindings.fish index de0f8bd6e..7d2623259 100644 --- a/share/functions/fish_vi_key_bindings.fish +++ b/share/functions/fish_vi_key_bindings.fish @@ -1,223 +1,224 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' - # The default escape timeout is 300ms. But for users of Vi bindings that can - # be slightly annoying when trying to switch to Vi "normal" mode. Too, - # vi-mode users are unlikely to use escape-as-meta. So set a much shorter - # timeout in this case. - set -q fish_escape_delay_ms; or set -g fish_escape_delay_ms 10 + # The default escape timeout is 300ms. But for users of Vi bindings that can + # be slightly annoying when trying to switch to Vi "normal" mode. Too, + # vi-mode users are unlikely to use escape-as-meta. So set a much shorter + # timeout in this case. + set -q fish_escape_delay_ms; or set -g fish_escape_delay_ms 10 - set -l init_mode insert - set -l eol_keys \$ g\$ \e\[F - set -l bol_keys \^ 0 g\^ \e\[H - if set -q argv[1] - set init_mode $argv[1] - end + set -l init_mode insert + set -l eol_keys \$ g\$ \e\[F + set -l bol_keys \^ 0 g\^ \e\[H + if set -q argv[1] + set init_mode $argv[1] + end - # Inherit default key bindings. - # Do this first so vi-bindings win over default. - bind --erase --all - fish_default_key_bindings -M insert - fish_default_key_bindings -M default + # Inherit default key bindings. + # Do this first so vi-bindings win over default. + bind --erase --all + fish_default_key_bindings -M insert + fish_default_key_bindings -M default - # Add a way to switch from insert to normal (command) mode. - bind -M insert -m default \cc force-repaint - bind -M insert -m default \e backward-char force-repaint + # Add way to kill current command line while in insert mode. + bind -M insert \cc 'commandline ""' + # Add a way to switch from insert to normal (command) mode. + bind -M insert -m default \e backward-char force-repaint - # - # normal (command) mode - # - bind :q exit - bind \cd exit - bind \cc 'commandline ""' - bind h backward-char - bind l forward-char - bind \e\[C forward-char - bind \e\[D backward-char + # + # normal (command) mode + # + bind :q exit + bind \cd exit + bind \cc 'commandline ""' + bind h backward-char + bind l forward-char + bind \e\[C forward-char + bind \e\[D backward-char - # Some linux VTs output these (why?) - bind \eOC forward-char - bind \eOD backward-char + # Some linux VTs output these (why?) + bind \eOC forward-char + bind \eOD backward-char - bind -k right forward-char - bind -k left backward-char - bind -m insert \n execute - bind -m insert \r execute - bind -m insert i force-repaint - bind -m insert I beginning-of-line force-repaint - bind -m insert a forward-char force-repaint - bind -m insert A end-of-line force-repaint - bind -m visual v begin-selection force-repaint + bind -k right forward-char + bind -k left backward-char + bind -m insert \n execute + bind -m insert \r execute + bind -m insert i force-repaint + bind -m insert I beginning-of-line force-repaint + bind -m insert a forward-char force-repaint + bind -m insert A end-of-line force-repaint + bind -m visual v begin-selection force-repaint - #bind -m insert o "commandline -a \n" down-line force-repaint - #bind -m insert O beginning-of-line "commandline -i \n" up-line force-repaint # doesn't work + #bind -m insert o "commandline -a \n" down-line force-repaint + #bind -m insert O beginning-of-line "commandline -i \n" up-line force-repaint # doesn't work - bind gg beginning-of-buffer - bind G end-of-buffer + bind gg beginning-of-buffer + bind G end-of-buffer - for key in $eol_keys - bind $key end-of-line - end - for key in $bol_keys - bind $key beginning-of-line - end + for key in $eol_keys + bind $key end-of-line + end + for key in $bol_keys + bind $key beginning-of-line + end - bind u history-search-backward - bind \cr history-search-forward + bind u history-search-backward + bind \cr history-search-forward - bind [ history-token-search-backward - bind ] history-token-search-forward + bind [ history-token-search-backward + bind ] history-token-search-forward - bind k up-or-search - bind j down-or-search - bind \e\[A up-or-search - bind \e\[B down-or-search - bind -k down down-or-search - bind -k up up-or-search - bind \eOA up-or-search - bind \eOB down-or-search + bind k up-or-search + bind j down-or-search + bind \e\[A up-or-search + bind \e\[B down-or-search + bind -k down down-or-search + bind -k up up-or-search + bind \eOA up-or-search + bind \eOB down-or-search - bind b backward-word - bind B backward-bigword - bind ge backward-word - bind gE backward-bigword - bind w forward-word forward-char - bind W forward-bigword forward-char - bind e forward-char forward-word backward-char - bind E forward-bigword backward-char + bind b backward-word + bind B backward-bigword + bind ge backward-word + bind gE backward-bigword + bind w forward-word forward-char + bind W forward-bigword forward-char + bind e forward-char forward-word backward-char + bind E forward-bigword backward-char - bind x delete-char - bind X backward-delete-char + bind x delete-char + bind X backward-delete-char - bind -k dc delete-char + bind -k dc delete-char - bind -k backspace backward-delete-char - bind \x7f backward-delete-char - bind \e\[3\;2~ backward-delete-char # Mavericks Terminal.app shift-delete + bind -k backspace backward-delete-char + bind \x7f backward-delete-char + bind \e\[3\;2~ backward-delete-char # Mavericks Terminal.app shift-delete - bind dd kill-whole-line - bind D kill-line - bind d\$ kill-line - bind d\^ backward-kill-line - bind dw kill-word - bind dW kill-bigword - bind diw forward-char forward-char backward-word kill-word - bind diW forward-char forward-char backward-bigword kill-bigword - bind daw forward-char forward-char backward-word kill-word - bind daW forward-char forward-char backward-bigword kill-bigword - bind de kill-word - bind dE kill-bigword - bind db backward-kill-word - bind dB backward-kill-bigword - bind dge backward-kill-word - bind dgE backward-kill-bigword + bind dd kill-whole-line + bind D kill-line + bind d\$ kill-line + bind d\^ backward-kill-line + bind dw kill-word + bind dW kill-bigword + bind diw forward-char forward-char backward-word kill-word + bind diW forward-char forward-char backward-bigword kill-bigword + bind daw forward-char forward-char backward-word kill-word + bind daW forward-char forward-char backward-bigword kill-bigword + bind de kill-word + bind dE kill-bigword + bind db backward-kill-word + bind dB backward-kill-bigword + bind dge backward-kill-word + bind dgE backward-kill-bigword - bind -m insert s delete-char force-repaint - bind -m insert S kill-whole-line force-repaint - bind -m insert cc kill-whole-line force-repaint - bind -m insert C kill-line force-repaint - bind -m insert c\$ kill-line force-repaint - bind -m insert c\^ backward-kill-line force-repaint - bind -m insert cw kill-word force-repaint - bind -m insert cW kill-bigword force-repaint - bind -m insert ciw forward-char forward-char backward-word kill-word force-repaint - bind -m insert ciW forward-char forward-char backward-bigword kill-bigword force-repaint - bind -m insert caw forward-char forward-char backward-word kill-word force-repaint - bind -m insert caW forward-char forward-char backward-bigword kill-bigword force-repaint - bind -m insert ce kill-word force-repaint - bind -m insert cE kill-bigword force-repaint - bind -m insert cb backward-kill-word force-repaint - bind -m insert cB backward-kill-bigword force-repaint - bind -m insert cge backward-kill-word force-repaint - bind -m insert cgE backward-kill-bigword force-repaint + bind -m insert s delete-char force-repaint + bind -m insert S kill-whole-line force-repaint + bind -m insert cc kill-whole-line force-repaint + bind -m insert C kill-line force-repaint + bind -m insert c\$ kill-line force-repaint + bind -m insert c\^ backward-kill-line force-repaint + bind -m insert cw kill-word force-repaint + bind -m insert cW kill-bigword force-repaint + bind -m insert ciw forward-char forward-char backward-word kill-word force-repaint + bind -m insert ciW forward-char forward-char backward-bigword kill-bigword force-repaint + bind -m insert caw forward-char forward-char backward-word kill-word force-repaint + bind -m insert caW forward-char forward-char backward-bigword kill-bigword force-repaint + bind -m insert ce kill-word force-repaint + bind -m insert cE kill-bigword force-repaint + bind -m insert cb backward-kill-word force-repaint + bind -m insert cB backward-kill-bigword force-repaint + bind -m insert cge backward-kill-word force-repaint + bind -m insert cgE backward-kill-bigword force-repaint - bind '~' capitalize-word - bind gu downcase-word - bind gU upcase-word + bind '~' capitalize-word + bind gu downcase-word + bind gU upcase-word - bind J end-of-line delete-char - bind K 'man (commandline -t) ^/dev/null; or echo -n \a' + bind J end-of-line delete-char + bind K 'man (commandline -t) ^/dev/null; or echo -n \a' - bind yy kill-whole-line yank - bind Y kill-whole-line yank - bind y\$ kill-line yank - bind y\^ backward-kill-line yank - bind yw kill-word yank - bind yW kill-bigword yank - bind yiw forward-char forward-char backward-word kill-word yank - bind yiW forward-char forward-char backward-bigword kill-bigword yank - bind yaw forward-char forward-char backward-word kill-word yank - bind yaW forward-char forward-char backward-bigword kill-bigword yank - bind ye kill-word yank - bind yE kill-bigword yank - bind yb backward-kill-word yank - bind yB backward-kill-bigword yank - bind yge backward-kill-word yank - bind ygE backward-kill-bigword yank + bind yy kill-whole-line yank + bind Y kill-whole-line yank + bind y\$ kill-line yank + bind y\^ backward-kill-line yank + bind yw kill-word yank + bind yW kill-bigword yank + bind yiw forward-char forward-char backward-word kill-word yank + bind yiW forward-char forward-char backward-bigword kill-bigword yank + bind yaw forward-char forward-char backward-word kill-word yank + bind yaW forward-char forward-char backward-bigword kill-bigword yank + bind ye kill-word yank + bind yE kill-bigword yank + bind yb backward-kill-word yank + bind yB backward-kill-bigword yank + bind yge backward-kill-word yank + bind ygE backward-kill-bigword yank - bind f forward-jump - bind F backward-jump - bind t forward-jump and backward-char - bind T backward-jump and forward-char + bind f forward-jump + bind F backward-jump + bind t forward-jump and backward-char + bind T backward-jump and forward-char - # in emacs yank means paste - bind p yank - bind P backward-char yank - bind gp yank-pop + # in emacs yank means paste + bind p yank + bind P backward-char yank + bind gp yank-pop - ### Overrides - # This is complete in vim - bind -M insert \cx end-of-line + ### Overrides + # This is complete in vim + bind -M insert \cx end-of-line - bind '"*p' "commandline -i ( xsel -p; echo )[1]" - bind '"*P' backward-char "commandline -i ( xsel -p; echo )[1]" + bind '"*p' "commandline -i ( xsel -p; echo )[1]" + bind '"*P' backward-char "commandline -i ( xsel -p; echo )[1]" - # - # Lowercase r, enters replace-one mode - # - bind -m replace-one r force-repaint - bind -M replace-one -m default '' delete-char self-insert backward-char force-repaint - bind -M replace-one -m default \e cancel force-repaint + # + # Lowercase r, enters replace-one mode + # + bind -m replace-one r force-repaint + bind -M replace-one -m default '' delete-char self-insert backward-char force-repaint + bind -M replace-one -m default \e cancel force-repaint - # - # visual mode - # - bind -M visual \e\[C forward-char - bind -M visual \e\[D backward-char - bind -M visual -k right forward-char - bind -M visual -k left backward-char - bind -M insert \eOC forward-char - bind -M insert \eOD backward-char - bind -M visual h backward-char - bind -M visual l forward-char + # + # visual mode + # + bind -M visual \e\[C forward-char + bind -M visual \e\[D backward-char + bind -M visual -k right forward-char + bind -M visual -k left backward-char + bind -M insert \eOC forward-char + bind -M insert \eOD backward-char + bind -M visual h backward-char + bind -M visual l forward-char - bind -M visual k up-line - bind -M visual j down-line + bind -M visual k up-line + bind -M visual j down-line - bind -M visual b backward-word - bind -M visual B backward-bigword - bind -M visual ge backward-word - bind -M visual gE backward-bigword - bind -M visual w forward-word - bind -M visual W forward-bigword - bind -M visual e forward-word - bind -M visual E forward-bigword - bind -M visual o swap-selection-start-stop force-repaint + bind -M visual b backward-word + bind -M visual B backward-bigword + bind -M visual ge backward-word + bind -M visual gE backward-bigword + bind -M visual w forward-word + bind -M visual W forward-bigword + bind -M visual e forward-word + bind -M visual E forward-bigword + bind -M visual o swap-selection-start-stop force-repaint - for key in $eol_keys - bind -M visual $key end-of-line - end - for key in $bol_keys - bind -M visual $key beginning-of-line - end + for key in $eol_keys + bind -M visual $key end-of-line + end + for key in $bol_keys + bind -M visual $key beginning-of-line + end - bind -M visual -m insert c kill-selection end-selection force-repaint - bind -M visual -m default d kill-selection end-selection force-repaint - bind -M visual -m default x kill-selection end-selection force-repaint - bind -M visual -m default X kill-whole-line end-selection force-repaint - bind -M visual -m default y kill-selection yank end-selection force-repaint - bind -M visual -m default '"*y' "commandline -s | xsel -p" end-selection force-repaint + bind -M visual -m insert c kill-selection end-selection force-repaint + bind -M visual -m default d kill-selection end-selection force-repaint + bind -M visual -m default x kill-selection end-selection force-repaint + bind -M visual -m default X kill-whole-line end-selection force-repaint + bind -M visual -m default y kill-selection yank end-selection force-repaint + bind -M visual -m default '"*y' "commandline -s | xsel -p" end-selection force-repaint - bind -M visual -m default \cc end-selection force-repaint - bind -M visual -m default \e end-selection force-repaint + bind -M visual -m default \cc end-selection force-repaint + bind -M visual -m default \e end-selection force-repaint - set fish_bind_mode $init_mode + set fish_bind_mode $init_mode end From a148b755a66f050c739aa9844daaea72149e3543 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Tue, 29 Mar 2016 16:42:58 -0700 Subject: [PATCH 041/363] more fish_indent cleanup for prev commit Commit dfb23c4fce11d9d0577a2cb3c9e192bc24f64c8c was supposed to incude all the edits to make the code compliant with fish_indent. --- share/functions/fish_vi_key_bindings.fish | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/share/functions/fish_vi_key_bindings.fish b/share/functions/fish_vi_key_bindings.fish index 7d2623259..75be6644b 100644 --- a/share/functions/fish_vi_key_bindings.fish +++ b/share/functions/fish_vi_key_bindings.fish @@ -3,13 +3,14 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' # be slightly annoying when trying to switch to Vi "normal" mode. Too, # vi-mode users are unlikely to use escape-as-meta. So set a much shorter # timeout in this case. - set -q fish_escape_delay_ms; or set -g fish_escape_delay_ms 10 + set -q fish_escape_delay_ms + or set -g fish_escape_delay_ms 10 set -l init_mode insert set -l eol_keys \$ g\$ \e\[F set -l bol_keys \^ 0 g\^ \e\[H if set -q argv[1] - set init_mode $argv[1] + set init_mode $argv[1] end # Inherit default key bindings. @@ -55,10 +56,10 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' bind G end-of-buffer for key in $eol_keys - bind $key end-of-line + bind $key end-of-line end for key in $bol_keys - bind $key beginning-of-line + bind $key beginning-of-line end bind u history-search-backward @@ -138,7 +139,7 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' bind K 'man (commandline -t) ^/dev/null; or echo -n \a' bind yy kill-whole-line yank - bind Y kill-whole-line yank + bind Y kill-whole-line yank bind y\$ kill-line yank bind y\^ backward-kill-line yank bind yw kill-word yank @@ -204,13 +205,13 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' bind -M visual o swap-selection-start-stop force-repaint for key in $eol_keys - bind -M visual $key end-of-line + bind -M visual $key end-of-line end for key in $bol_keys - bind -M visual $key beginning-of-line + bind -M visual $key beginning-of-line end - bind -M visual -m insert c kill-selection end-selection force-repaint + bind -M visual -m insert c kill-selection end-selection force-repaint bind -M visual -m default d kill-selection end-selection force-repaint bind -M visual -m default x kill-selection end-selection force-repaint bind -M visual -m default X kill-whole-line end-selection force-repaint @@ -218,7 +219,7 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' bind -M visual -m default '"*y' "commandline -s | xsel -p" end-selection force-repaint bind -M visual -m default \cc end-selection force-repaint - bind -M visual -m default \e end-selection force-repaint + bind -M visual -m default \e end-selection force-repaint set fish_bind_mode $init_mode end From 0e18e2ba7870bcd907cd804e35062ad49fcb01d4 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Tue, 29 Mar 2016 15:23:42 -0700 Subject: [PATCH 042/363] clarify behavior of ** glob Fixes #2680 --- doc_src/index.hdr.in | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index e4eabf48d..15c67c379 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -399,7 +399,19 @@ If a star (`*`) or a question mark (`?`) is present in the parameter, `fish` att - `*` can match any string of characters not containing '/'. This includes matching an empty string. -- `**` matches any string of characters. This includes matching an empty string. The string may include the `/` character but does not need to. +- `**` matches any string of characters. This includes matching an empty string. The matched string may include the `/` character; that is, it recurses into subdirectories. Note that augmenting this wildcard with other strings will not match files in the current working directory (`$PWD`) if you separate the strings with a slash ("/"). This is unlike other shells such as zsh. For example, `**\/*.fish` in zsh will match `.fish` files in the PWD but in fish will only match such files in a subdirectory. In fish you should type `***.fish` to match files in the PWD as well as subdirectories. + +Other shells, such as zsh, provide a rich glob syntax for restricting the files matched by globs. For example, `**(.)`, to only match regular files. Fish prefers to defer such features to programs, such as `find`, rather than reinventing the wheel. Thus, if you want to limit the wildcard expansion to just regular files the fish approach is to define and use a function. For example, + +\fish{cli-dark} +function ff --description 'Like ** but only returns plain files.' + # This also ignores .git directories. + find . \( -name .git -type d -prune \) -o -type f | \ + sed -n -e '/^\.\/\.git$/n' -e 's/^\.\///p' +end +\endfish + +You would then use it in place of `**` like this, `my_prog (ff)`, to pass only regular files in or below $PWD to `my_prog`. Wildcard matches are sorted case insensitively. When sorting matches containing numbers, consecutive digits are considered to be one element, so that the strings '1' '5' and '12' would be sorted in the order given. From aacdaee6a9b0587d87bc176553bf1489453a8530 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 29 Mar 2016 19:56:14 +0200 Subject: [PATCH 043/363] Add history --help This was probably an oversight - the builtin supports it, the function doesn't. --- share/functions/history.fish | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/share/functions/history.fish b/share/functions/history.fish index 758b5ea56..a26db0f4c 100644 --- a/share/functions/history.fish +++ b/share/functions/history.fish @@ -36,6 +36,8 @@ function history --description "Deletes an item from history" set cmd print case --merge set cmd merge + case --help + set cmd help case -- set -e argv[$i] break @@ -131,6 +133,8 @@ function history --description "Deletes an item from history" builtin history $argv case merge builtin history --merge + case help + builtin history --help case clear # Erase the entire history echo "Are you sure you want to clear history ? (y/n)" From 7e014174b876308f7a6105d6571f51ab7c2022f0 Mon Sep 17 00:00:00 2001 From: Sam Yu Date: Wed, 30 Mar 2016 15:39:55 +0800 Subject: [PATCH 044/363] __fish_print_packages: use libzypp builtin cache for zypper --- share/functions/__fish_print_packages.fish | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/share/functions/__fish_print_packages.fish b/share/functions/__fish_print_packages.fish index be7d17e1e..a6b84b475 100644 --- a/share/functions/__fish_print_packages.fish +++ b/share/functions/__fish_print_packages.fish @@ -54,6 +54,12 @@ function __fish_print_packages # Zypper needs caching as it is slow if type -q -f zypper + # Use libzypp cache file if available + if test -f /var/cache/zypp/solv/@System/solv.idx + cat /var/cache/zypp/solv/*/solv.idx | awk '!/application:|srcpackage:|product:|pattern:|patch:/ {print $1'\t$package'}' + return + end + # If the cache is less than five minutes old, we do not recalculate it set -l cache_file $XDG_CACHE_HOME/.zypper-cache.$USER From 35cee1e39c0ca6bd35f2bb0c18ad6ffca5cc7108 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Wed, 30 Mar 2016 15:35:14 -0700 Subject: [PATCH 045/363] remove "doc" make target and rename "user_doc" Fixes #2874 --- Makefile.in | 30 ++++++++++++------------------ build_tools/make_tarball.sh | 2 +- doc_src/FORMATTING.md | 2 +- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/Makefile.in b/Makefile.in index 056a3d814..c68d7aa5d 100644 --- a/Makefile.in +++ b/Makefile.in @@ -266,29 +266,23 @@ prof: all # # User documentation, describing the features of the fish shell. # - -# Depend on the sources (*.hdr.in) and manually make the -# intermediate *.hdr and doc.h files if needed -# The sed command deletes everything including and after the first -, for simpler version numbers -# Cleans up the user_doc/html directory once Doxygen is done. - -user_doc: $(HDR_FILES_SRC) Doxyfile.user $(HTML_SRC) $(HELP_SRC) doc.h $(HDR_FILES) lexicon_filter +# Depend on the sources (*.hdr.in) and manually make the intermediate *.hdr +# and doc.h files if needed. The sed command deletes everything including and +# after the first -, for simpler version numbers. Cleans up the user_doc/html +# directory once Doxygen is done. +# +doc: $(HDR_FILES_SRC) Doxyfile.user $(HTML_SRC) $(HELP_SRC) doc.h \ + $(HDR_FILES) lexicon_filter (cat Doxyfile.user; echo INPUT_FILTER=./lexicon_filter; \ - echo PROJECT_NUMBER=$(FISH_BUILD_VERSION) | $(SED) "s/-.*//") | doxygen - && touch user_doc; \ - cd user_doc/html && rm -f bc_s.png bdwn.png closed.png ftv2*.png nav*.png open.png sync_*.png tab*.* doxygen.* dynsections.js jquery.js pages.html - -# -# Source code documentation. Also includes user documentation. -# - -doc: src/*.h src/*.cpp doc.h Doxyfile lexicon_filter - (cat Doxyfile; echo INPUT_FILTER=./lexicon_filter; echo PROJECT_NUMBER=$(FISH_BUILD_VERSION)) | doxygen - ; - + echo PROJECT_NUMBER=$(FISH_BUILD_VERSION) | $(SED) "s/-.*//") | \ + doxygen - && touch user_doc; \ + cd user_doc/html && rm -f bc_s.png bdwn.png closed.png ftv2*.png \ + nav*.png open.png sync_*.png tab*.* doxygen.* dynsections.js \ + jquery.js pages.html # # PDF version of the source code documentation. # - doc/refman.pdf: doc cd doc/latex && \ make && \ diff --git a/build_tools/make_tarball.sh b/build_tools/make_tarball.sh index 4f02411bd..1e24de0f9 100755 --- a/build_tools/make_tarball.sh +++ b/build_tools/make_tarball.sh @@ -37,7 +37,7 @@ git archive --format=tar --prefix="$prefix"/ HEAD > "$path" # Don't run autoheader since configure.ac runs it. autoconf is enough. autoconf ./configure --with-doxygen -make user_doc share/man +make doc share/man echo $VERSION > version cd /tmp rm -f "$prefix" diff --git a/doc_src/FORMATTING.md b/doc_src/FORMATTING.md index 0a44f3554..0ae1c81e1 100644 --- a/doc_src/FORMATTING.md +++ b/doc_src/FORMATTING.md @@ -56,7 +56,7 @@ is transformed into: `@cmnd{echo} @args{hello} @args{world}` -which is then transformed by Doxygen into an HTML version (`make user_doc`): +which is then transformed by Doxygen into an HTML version (`make doc`): `echo hello world` From 6888c3d60495f7807468edf1c78ff2c817c64f31 Mon Sep 17 00:00:00 2001 From: Jak Wings Date: Thu, 31 Mar 2016 14:51:58 +0800 Subject: [PATCH 046/363] rename make target "user_doc" to "doc" Fixes #2875 --- Makefile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index c68d7aa5d..621d787f8 100644 --- a/Makefile.in +++ b/Makefile.in @@ -213,7 +213,7 @@ endif # ifeq ($(HAVE_DOXYGEN), 1) - user_doc=user_doc + user_doc=doc share_man=share/man else user_doc= From 83233ccc5a6ab8e7521133eebb771af6939750a8 Mon Sep 17 00:00:00 2001 From: Laurence McGlashan Date: Thu, 31 Mar 2016 22:19:27 +0100 Subject: [PATCH 047/363] Correction to make rule for fish_version --- Makefile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index 621d787f8..59eb13409 100644 --- a/Makefile.in +++ b/Makefile.in @@ -238,7 +238,7 @@ FISH-BUILD-VERSION-FILE: FORCE -include FISH-BUILD-VERSION-FILE CXXFLAGS += -DFISH_BUILD_VERSION=\"$(FISH_BUILD_VERSION)\" .PHONY: FORCE -fish_version.o: FISH-BUILD-VERSION-FILE +obj/fish_version.o: FISH-BUILD-VERSION-FILE # From 4f5d42858c558af73575b1f38e6ad0bc58912b46 Mon Sep 17 00:00:00 2001 From: David Adam Date: Fri, 1 Apr 2016 07:19:43 +0800 Subject: [PATCH 048/363] travis: move to clang 3.8 for ASan builds --- .travis.yml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index cc883e8c6..8307fe85f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,22 +18,19 @@ matrix: addons: apt: sources: - # stick with 3.6; the 3.7 Debian binaries do not support ASan yet - # https://llvm.org/bugs/show_bug.cgi?id=22757 - - llvm-toolchain-precise-3.6 + - llvm-toolchain-precise-3.8 - ubuntu-toolchain-r-test packages: - - clang-3.6 - - llvm-3.6 # for llvm-symbolizer + - clang-3.8 + - llvm-3.8 # for llvm-symbolizer - bc - expect - gettext - libncurses5-dev env: - CXXFLAGS="-g -fno-omit-frame-pointer -fsanitize=address" - - ASAN_SYMBOLIZER_PATH="/usr/bin/llvm-symbolizer-3.6" - ASAN_OPTIONS=check_initialization_order=1:detect_stack_use_after_return=1:detect_leaks=1 - before_install: export CXX=clang++-3.6 + before_install: export CXX=clang++-3.8 - os: osx before_install: From 6fa09e6a70d4116e22df36d33e2f7533cbcbe098 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Fri, 1 Apr 2016 16:28:36 -0700 Subject: [PATCH 049/363] add make targets to lint the code Fixes #2818 --- .oclint | 8 ++++ CONTRIBUTING.md | 95 ++++++++++++++++++++++--------------------- Makefile.in | 8 ++++ build_tools/lint.fish | 95 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 160 insertions(+), 46 deletions(-) create mode 100644 .oclint create mode 100755 build_tools/lint.fish diff --git a/.oclint b/.oclint new file mode 100644 index 000000000..cd68e1821 --- /dev/null +++ b/.oclint @@ -0,0 +1,8 @@ +rules: +rule-configurations: + # This is the default value (as of the time I wrote this) but I'm making + # it explicit since it needs to agree with the value used by clang-format. + # Thus, if we ever change the fish style to allow longer lines this should + # be changed (as well as the corresponding clang-format config). + - key: LONG_LINE + value: 100 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d3e2ca1f9..6321cdc7f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,15 +1,50 @@ -# Style guide +# Guidelines For Developers -This is style guide for fish contributors. You should use it for any new code -that you would add to this project and try to format existing code to use this -style. +## Lint Free Code -## Formatting +Automated analysis tools like cppcheck and oclint can help identify bugs. They also help ensure the code has a consistent style and avoids patterns that tend to confuse people. + +Ultimately we want lint free code. However, at the moment a lot of cleanup is required to reach that goal. For now simply try to avoid introducing new lint. + +To make linting the code easy there are two make targets: `lint` and `lint-all`. The latter does just what the name implies. The former will lint any modified but not committed `*.cpp` files. If there is no uncommitted work it will lint the files in the most recent commit. + +To install the lint checkers on Mac OS X using HomeBrew: + +``` +brew tap oclint/formulae +brew install oclint +brew install cppcheck +``` + +To install the lint checkers on Linux distros that use Apt: + +``` +sudo apt-get install clang +sudo apt-get install oclint +sudo apt-get install cppcheck +``` + +## Fish Script Style Guide + +Fish scripts such as those in the *share/functions* and *tests* directories should be formatted using the `fish_indent` command. + +Function names should be all lowercase with undescores separating words. Private functions should begin with an underscore. The first word should be `fish` if the function is unique to fish. + +The first word of global variable names should generally be `fish` for public vars or `_fish` for private vars to minimize the possibility of name clashes with user defined vars. + +## C++ Style Guide + +1. The `clang-format` command is authoritative with respect to indentation, whitespace around operators, etc. **Note**: this rule should be ignored at this time. A subsequent commit will add the necessary config file and make targets. After the happens the code will be cleaned up and this rule will become mandatory. + +1. All names in code should be `small_snake_case`. No Hungarian notation is used. Classes and structs names should be followed by `_t`. 1. fish uses the Allman/BSD style of indentation. -2. Indent with spaces, not tabs. -3. Use 4 spaces per indent (unless needed like `Makefile`). -4. Opening curly bracket is on the following line: + +1. Indent with spaces, not tabs. + +1. Use 4 spaces per indent. + +1. Opening curly bracket is on the following line: // ✔: struct name @@ -32,7 +67,7 @@ style. // code } -5. Put space after `if`, `while` and `for` before conditions. +1. Put space after `if`, `while` and `for` before conditions. // ✔: if () {} @@ -40,7 +75,7 @@ style. // ✗: if() {} -6. Put spaces before and after operators excluding increment and decrement; +1. Put spaces before and after operators excluding increment and decrement; // ✔: int a = 1 + 2 * 3; @@ -50,7 +85,7 @@ style. int a=1+2*3; a ++; -7. Never put spaces between function name and parameters list. +1. Never put spaces between function name and parameters list. // ✔: func(args); @@ -58,8 +93,9 @@ style. // ✗: func (args); -8. Never put spaces after `(` and before `)`. -9. Always put space after comma and semicolon. +1. Never put spaces after `(` and before `)`. + +1. Always put space after comma and semicolon. // ✔: func(arg1, arg2); @@ -70,36 +106,3 @@ style. func(arg1,arg2); for (int i = 0;i /dev/null + # There are pending changes so lint those files. + for arg in $pending + set files $files (string split -m 1 ' ' $arg)[2] + end + else + # No pending changes so lint the files in the most recent commit. + set files (git show --porcelain --name-only --pretty=oneline head | tail --lines=+2) + end + + # Filter out the non-C/C++ files. + set c_files (string match -r '.*\.c(?:pp)?$' -- $files) +else + set c_files src/*.cpp +end + +# We now have a list of files to check so run the linters. +if set -q c_files[1] + if type -q cppcheck + echo + echo ======================================== + echo Running cppcheck + echo ======================================== + cppcheck -q --verbose --std=posix --std=c11 --language=c++ \ + --inline-suppr --enable=$cppchecks $cppcheck_args $c_files + end + + if type -q oclint + echo + echo ======================================== + echo Running oclint + echo ======================================== + if test (uname -s) = "Darwin" + if not test -f compile_commands.json + xcodebuild > xcodebuild.log + oclint-xcodebuild xcodebuild.log > /dev/null + end + if test $all = yes + oclint-json-compilation-database -e '/pcre2-10.20/' \ + -- -enable-global-analysis + else + set i_files + for f in $c_files + set i_files $i_files -i $f + end + echo oclint-json-compilation-database -e '/pcre2-10.20/' $i_files + oclint-json-compilation-database -e '/pcre2-10.20/' $i_files + end + else + # Presumably we're on Linux or other platform not requiring special + # handling for oclint to work. + oclint $c_files -- $argv + end + end +else + echo + echo 'WARNING: No C/C++ files to check' + echo +end From bbf0007c46994e89d8d78954b5a0419ee0a60435 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Fri, 1 Apr 2016 21:01:54 -0700 Subject: [PATCH 050/363] remove create_wajig_completions.py script This script is really old and no longer works correctly. This discussion, http://comments.gmane.org/gmane.comp.shells.fish.user/4062, from over two years ago concluded it should be removed. Resolves #2881 --- create_wajig_completions.py | 121 ------------------------------------ 1 file changed, 121 deletions(-) delete mode 100755 create_wajig_completions.py diff --git a/create_wajig_completions.py b/create_wajig_completions.py deleted file mode 100755 index 15dfd1ab8..000000000 --- a/create_wajig_completions.py +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/env python -# -*- python -*- - -# Program to generate fish completion function for wajig. -# It runs 'wajig command' and analyzes the output to build a -# completion file which it writes to stdout. -# To use the result, direct stdout to -# ~/.fish.d/completions/wajig.fish. - -# Author Reuben Thomas, from Don Rozenberg's bash_completion.py and -# fish's apt-get.fish. - -import os -import re -import pprint -pp = pprint.PrettyPrinter() - -def escape_quotes(s): - return re.sub('\'', '\\\'', s) - -# Run wajig command -f = os.popen('wajig commands', 'r') - -lines = f.readlines() - -option_patt = r'^-([a-z]*)\|--([a-z]*) +([^ ].*)' -option_patt_r = re.compile(option_patt) - -command_patt = r'^([a-z-]*) +([^ ].*)' -command_patt_r = re.compile(command_patt) - -os_str = [] -os_str.append('') -ol_str = [] -ol_str.append('') -oh_str = [] -oh_str.append('') -o_i = 0 - -c_str = [] -c_str.append('') -ch_str = [] -ch_str.append('') -c_i = 0 - -for l in lines: - l = l.strip() - if l == '' or l.find(':') > -1 or l.find('Run') == 0: - continue - if l.find('-') == 0: - mo = option_patt_r.search(l) - if mo == None: - continue - os_str[o_i] = mo.group(1) - os_str.append('') - ol_str[o_i] = mo.group(2) - ol_str.append('') - oh_str[o_i] = escape_quotes(mo.group(3)) - oh_str.append('') - o_i += 1 - else: - mo = command_patt_r.search(l) - if mo == None: - continue - c_str[c_i] = mo.group(1) - c_str.append('') - ch_str[c_i] = escape_quotes(mo.group(2)) - ch_str.append('') - c_i += 1 - -# For debugging, print the commands and options. -#print -#pp.pprint(c_str) - -#print -#pp.pprint(os_str) -#print -#pp.pprint(ol_str) - -part1 = '''function __fish_wajig_no_subcommand -d (N_ 'Test if wajig has yet to be given the subcommand') - for i in (commandline -opc) - if contains -- $i''' - -part2 = ''' - return 1 - end - end - return 0 -end - -function __fish_wajig_use_package -d (N_ 'Test if wajig command should have packages as potential completion') - for i in (commandline -opc) - if contains -- $i contains bug build build-depend changelog dependents describe detail hold install installr installrs installs list list-files news package purge purge-depend readme recursive recommended reconfigure reinstall remove remove-depend repackage show showinstall showremove showupgrade size sizes source suggested unhold upgrade versions whatis - return 0 - end - end - return 1 -end - -complete -c wajig -n '__fish_wajig_use_package' -a '(__fish_print_packages)' -d (N_ 'Package')''' - -wajig = part1 - -#add the commands. -for i in range(0, len(c_str) - 1): - wajig = "%s %s" % (wajig, c_str[i]) - -#add part2 -wajig = "%s%s" % (wajig, part2) - -#add the options. -wajig = "%s%s" % (wajig, os_str[0].lstrip()) -for i in range(1, len(os_str) - 1): - wajig = "%s\ncomplete -c apt-get -s %s -l %s -d (N_ '%s')" % (wajig, os_str[i], ol_str[i], oh_str[i]) - -#add the commands. -for i in range(0, len(c_str) - 1): - wajig = "%s\ncomplete -f -n '__fish_wajig_no_subcommand' -c wajig -a '%s' -d(N_ '%s')" % (wajig, c_str[i], ch_str[i]) - -#print it all -print wajig From 3f1fc332e7451187b1b9806f544bc8199fa412a8 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sat, 2 Apr 2016 22:18:31 -0700 Subject: [PATCH 051/363] remove unused wgetopt() method found by linting --- src/wgetopt.cpp | 8 -------- src/wgetopt.h | 1 - 2 files changed, 9 deletions(-) diff --git a/src/wgetopt.cpp b/src/wgetopt.cpp index 80ffdcc90..c2905ab0a 100644 --- a/src/wgetopt.cpp +++ b/src/wgetopt.cpp @@ -573,14 +573,6 @@ int wgetopter_t::_wgetopt_internal(int argc, wchar_t **argv, const wchar_t *opts } } -int wgetopter_t::wgetopt(int argc, wchar_t **argv, const wchar_t *optstring) -{ - return _wgetopt_internal(argc, argv, optstring, - (const struct woption *) 0, - (int *) 0, - 0); -} - int wgetopter_t::wgetopt_long(int argc, wchar_t **argv, const wchar_t *options, const struct woption *long_options, int *opt_index) { return _wgetopt_internal(argc, argv, options, long_options, opt_index, 0); diff --git a/src/wgetopt.h b/src/wgetopt.h index ad1558703..794ccc652 100644 --- a/src/wgetopt.h +++ b/src/wgetopt.h @@ -147,7 +147,6 @@ class wgetopter_t { } - int wgetopt(int argc, wchar_t **argv, const wchar_t *optstring); int wgetopt_long(int argc, wchar_t **argv, const wchar_t *options, const struct woption *long_options, int *opt_index); int wgetopt_long_only(int argc, wchar_t **argv, const wchar_t *options, const struct woption *long_options, int *opt_index); }; From 9dd6873e5855f69b01f2e3705a49a4231c992e71 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sun, 3 Apr 2016 19:02:46 -0700 Subject: [PATCH 052/363] lint: remove or comment out unused functions Cppcheck has identified a lot of unused functions. This removes funcs that are unlikely to ever be used. Others that might be useful for debugging I've commented out with "#if 0". --- src/builtin.cpp | 4 ++++ src/common.cpp | 5 ----- src/common.h | 3 --- src/expand.cpp | 56 ---------------------------------------------- src/expand.h | 1 - src/fish_tests.cpp | 8 ++++--- src/io.cpp | 4 ++++ src/io.h | 4 +++- src/parser.cpp | 2 -- src/proc.cpp | 12 +++++++--- 10 files changed, 25 insertions(+), 74 deletions(-) diff --git a/src/builtin.cpp b/src/builtin.cpp index f4de72c73..dd67817a3 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -3925,6 +3925,8 @@ static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **ar return STATUS_BUILTIN_ERROR; } +#if 0 +// Disabled for the 2.2.0 release: https://github.com/fish-shell/fish-shell/issues/1809. int builtin_parse(parser_t &parser, io_streams_t &streams, wchar_t **argv) { struct sigaction act; @@ -3966,6 +3968,7 @@ int builtin_parse(parser_t &parser, io_streams_t &streams, wchar_t **argv) } return STATUS_BUILTIN_OK; } +#endif int builtin_true(parser_t &parser, io_streams_t &streams, wchar_t **argv) { @@ -3992,6 +3995,7 @@ static const builtin_data_t builtin_datas[]= { { L"[", &builtin_test, N_(L"Test a condition") }, #if 0 + // Disabled for the 2.2.0 release: https://github.com/fish-shell/fish-shell/issues/1809. { L"__fish_parse", &builtin_parse, N_(L"Try out the new parser") }, #endif { L"and", &builtin_generic, N_(L"Execute command if previous command suceeded") }, diff --git a/src/common.cpp b/src/common.cpp index a796baca1..84c3de65a 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -689,11 +689,6 @@ void debug(int level, const char *msg, ...) errno = errno_old; } -void print_stderr(const wcstring &str) -{ - fprintf(stderr, "%ls\n", str.c_str()); -} - void read_ignore(int fd, void *buff, size_t count) { size_t ignore __attribute__((unused)); diff --git a/src/common.h b/src/common.h index 74640988c..d6eb01910 100644 --- a/src/common.h +++ b/src/common.h @@ -811,9 +811,6 @@ ssize_t read_loop(int fd, void *buff, size_t count); void debug(int level, const char *msg, ...); void debug(int level, const wchar_t *msg, ...); -/** Writes a string to stderr, followed by a newline */ -void print_stderr(const wcstring &str); - /** Replace special characters with backslash escape sequences. Newline is replaced with \n, etc. diff --git a/src/expand.cpp b/src/expand.cpp index c2b4258c8..de67ffa84 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -1998,62 +1998,6 @@ bool fish_xdm_login_hack_hack_hack_hack(std::vector *cmds, int argc return result; } -bool fish_openSUSE_dbus_hack_hack_hack_hack(std::vector *args) -{ - static signed char isSUSE = -1; - if (isSUSE == 0) - return false; - - bool result = false; - if (args && ! args->empty()) - { - const wcstring &cmd = args->at(0).completion; - if (cmd.find(L"DBUS_SESSION_BUS_") != wcstring::npos) - { - /* See if we are SUSE */ - if (isSUSE < 0) - { - struct stat buf = {}; - isSUSE = (0 == stat("/etc/SuSE-release", &buf)); - } - - if (isSUSE) - { - /* Look for an equal sign */ - size_t where = cmd.find(L'='); - if (where != wcstring::npos) - { - /* Oh my. It's presumably of the form foo=bar; find the = and split */ - const wcstring key = wcstring(cmd, 0, where); - - /* Trim whitespace and semicolon */ - wcstring val = wcstring(cmd, where+1); - size_t last_good = val.find_last_not_of(L"\n ;"); - if (last_good != wcstring::npos) - val.resize(last_good + 1); - - args->clear(); - append_completion(args, L"set"); - if (key == L"DBUS_SESSION_BUS_ADDRESS") - append_completion(args, L"-x"); - append_completion(args, key); - append_completion(args, val); - result = true; - } - else if (string_prefixes_string(L"export DBUS_SESSION_BUS_ADDRESS;", cmd)) - { - /* Nothing, we already exported it */ - args->clear(); - append_completion(args, L"echo"); - append_completion(args, L"-n"); - result = true; - } - } - } - } - return result; -} - bool expand_abbreviation(const wcstring &src, wcstring *output) { if (src.empty()) diff --git a/src/expand.h b/src/expand.h index 731acd711..6ae91cca3 100644 --- a/src/expand.h +++ b/src/expand.h @@ -168,7 +168,6 @@ bool expand_abbreviation(const wcstring &src, wcstring *output); /* Terrible hacks */ bool fish_xdm_login_hack_hack_hack_hack(std::vector *cmds, int argc, const char * const *argv); -bool fish_openSUSE_dbus_hack_hack_hack_hack(std::vector *args); #endif diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 5bacab80e..9afb43441 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -2887,8 +2887,7 @@ public: static void test_history(void); static void test_history_merge(void); static void test_history_formats(void); - static void test_history_speed(void); - + // static void test_history_speed(void); static void test_history_races(void); static void test_history_races_pound_on_history(); }; @@ -3378,6 +3377,8 @@ void history_tests_t::test_history_formats(void) } } +#if 0 +// This test isn't run at this time. It was added by commit b9283d48 but not actually enabled. void history_tests_t::test_history_speed(void) { say(L"Testing history speed (pid is %d)", getpid()); @@ -3403,6 +3404,7 @@ void history_tests_t::test_history_speed(void) hist->clear(); delete hist; } +#endif static void test_new_parser_correctness(void) { @@ -4483,8 +4485,8 @@ int main(int argc, char **argv) if (should_test_function("history_merge")) history_tests_t::test_history_merge(); if (should_test_function("history_races")) history_tests_t::test_history_races(); if (should_test_function("history_formats")) history_tests_t::test_history_formats(); - //history_tests_t::test_history_speed(); if (should_test_function("string")) test_string(); + // history_tests_t::test_history_speed(); say(L"Encountered %d errors in low-level tests", err_count); if (s_test_run_count == 0) diff --git a/src/io.cpp b/src/io.cpp index da326f559..614528305 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -183,6 +183,9 @@ void io_chain_t::append(const io_chain_t &chain) this->insert(this->end(), chain.begin(), chain.end()); } +#if 0 +// This isn't used so the lint tools were complaining about its presence. I'm keeping it in the +// source because it could be useful for debugging. void io_print(const io_chain_t &chain) { if (chain.empty()) @@ -206,6 +209,7 @@ void io_print(const io_chain_t &chain) } } } +#endif /* If the given fd is used by the io chain, duplicates it repeatedly until an fd not used in the io chain is found, or we run out. If we return a new fd or an error, closes the old one. Any fd created is marked close-on-exec. Returns -1 on failure (in which case the given fd is still closed). */ static int move_fd_to_unused(int fd, const io_chain_t &io_chain) diff --git a/src/io.h b/src/io.h index adc59be17..181099adf 100644 --- a/src/io.h +++ b/src/io.h @@ -304,7 +304,9 @@ struct io_streams_t } }; -/** Print debug information about the specified IO redirection chain to stderr. */ +#if 0 +// Print debug information about the specified IO redirection chain to stderr. void io_print(const io_chain_t &chain); +#endif #endif diff --git a/src/parser.cpp b/src/parser.cpp index 2097aecf6..2862df609 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -842,8 +842,6 @@ int parser_t::eval_acquiring_tree(const wcstring &cmd, const io_chain_t &io, enu return 0; } - //print_stderr(block_stack_description()); - /* Determine the initial eval level. If this is the first context, it's -1; otherwise it's the eval level of the top context. This is sort of wonky because we're stitching together a global notion of eval level from these separate objects. A better approach would be some profile object that all contexts share, and that tracks the eval levels on its own. */ int exec_eval_level = (execution_contexts.empty() ? -1 : execution_contexts.back()->current_eval_level()); diff --git a/src/proc.cpp b/src/proc.cpp index f5c833e2a..1a0d64792 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -94,15 +94,21 @@ size_t job_iterator_t::count() const return this->job_list->size(); } +#if 0 +// This isn't used so the lint tools were complaining about its presence. I'm keeping it in the +// source because it could be useful for debugging. However, it would probably be better to add a +// verbose or debug option to the builtin `jobs` command. void print_jobs(void) { job_iterator_t jobs; job_t *j; - while ((j = jobs.next())) - { - printf("%p -> %ls -> (foreground %d, complete %d, stopped %d, constructed %d)\n", j, j->command_wcstr(), job_get_flag(j, JOB_FOREGROUND), job_is_completed(j), job_is_stopped(j), job_get_flag(j, JOB_CONSTRUCTED)); + while (j = jobs.next()) { + printf("%p -> %ls -> (foreground %d, complete %d, stopped %d, constructed %d)\n", + j, j->command_wcstr(), job_get_flag(j, JOB_FOREGROUND), job_is_completed(j), + job_is_stopped(j), job_get_flag(j, JOB_CONSTRUCTED)); } } +#endif int is_interactive_session=0; int is_subshell=0; From 411d573ba986af636e01dc07796954385b8ba4c7 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sun, 3 Apr 2016 20:23:43 -0700 Subject: [PATCH 053/363] convert atypical env_universal_common logging This is a quick and dirty conversion of the atypical, and undocumented, logging done by env_universal_common.cpp to the usual `debug()` pattern. I didn't want to drop the messages because they could be useful when debugging future issues. So I simply converted them to the lowest debug level using the normal debug() function. Fixes #2887 --- src/env_universal_common.cpp | 34 ++++++++++++++-------------------- src/env_universal_common.h | 6 ------ 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/src/env_universal_common.cpp b/src/env_universal_common.cpp index 71fb1667d..a3815886a 100644 --- a/src/env_universal_common.cpp +++ b/src/env_universal_common.cpp @@ -498,7 +498,7 @@ void env_universal_t::load_from_fd(int fd, callback_data_list_t *callbacks) const file_id_t current_file = file_id_for_fd(fd); if (current_file == last_read_file) { - UNIVERSAL_LOG("Sync elided based on fstat()"); + debug(5, L"universal log sync elided based on fstat()"); } else { @@ -525,7 +525,7 @@ bool env_universal_t::load_from_path(const wcstring &path, callback_data_list_t /* Check to see if the file is unchanged. We do this again in load_from_fd, but this avoids opening the file unnecessarily. */ if (last_read_file != kInvalidFileID && file_id_for_path(path) == last_read_file) { - UNIVERSAL_LOG("Sync elided based on fast stat()"); + debug(5, L"universal log sync elided based on fast stat()"); return true; } @@ -533,7 +533,7 @@ bool env_universal_t::load_from_path(const wcstring &path, callback_data_list_t int fd = wopen_cloexec(path, O_RDONLY); if (fd >= 0) { - UNIVERSAL_LOG("Reading from file"); + debug(5, L"universal log reading from file"); this->load_from_fd(fd, callbacks); close(fd); result = true; @@ -788,7 +788,7 @@ bool env_universal_t::open_and_acquire_lock(const wcstring &path, int *out_fd) /* Returns true if modified variables were written, false if not. (There may still be variable changes due to other processes on a false return). */ bool env_universal_t::sync(callback_data_list_t *callbacks) { - UNIVERSAL_LOG("sync"); + debug(5, L"universal log sync"); scoped_lock locker(lock); /* Our saving strategy: @@ -810,7 +810,7 @@ bool env_universal_t::sync(callback_data_list_t *callbacks) const wcstring &vars_path = explicit_vars_path.empty() ? default_vars_path() : explicit_vars_path; if (vars_path.empty()) { - debug(2, "No universal variable path available"); + debug(2, L"No universal variable path available"); return false; } @@ -818,7 +818,7 @@ bool env_universal_t::sync(callback_data_list_t *callbacks) if (modified.empty()) { this->load_from_path(vars_path, callbacks); - UNIVERSAL_LOG("No modifications"); + debug(5, L"universal log no modifications"); return false; } @@ -828,13 +828,13 @@ bool env_universal_t::sync(callback_data_list_t *callbacks) int private_fd = -1; wcstring private_file_path; - UNIVERSAL_LOG("Performing full sync"); + debug(5, L"universal log performing full sync"); /* Open the file */ if (success) { success = this->open_and_acquire_lock(vars_path, &vars_fd); - if (! success) UNIVERSAL_LOG("open_and_acquire_lock() failed"); + if (! success) debug(5, L"universal log open_and_acquire_lock() failed"); } /* Read from it */ @@ -849,7 +849,7 @@ bool env_universal_t::sync(callback_data_list_t *callbacks) if (success) { success = this->open_temporary_file(directory, &private_file_path, &private_fd); - if (! success) UNIVERSAL_LOG("open_temporary_file() failed"); + if (! success) debug(5, L"universal log open_temporary_file() failed"); } /* Write to it */ @@ -857,7 +857,7 @@ bool env_universal_t::sync(callback_data_list_t *callbacks) { assert(private_fd >= 0); success = this->write_to_fd(private_fd, private_file_path); - if (! success) UNIVERSAL_LOG("write_to_fd() failed"); + if (! success) debug(5, L"universal log write_to_fd() failed"); } if (success) @@ -867,9 +867,9 @@ bool env_universal_t::sync(callback_data_list_t *callbacks) if (wstat(vars_path, &sbuf) >= 0) { if (fchown(private_fd, sbuf.st_uid, sbuf.st_gid) == -1) - UNIVERSAL_LOG("fchown() failed"); + debug(5, L"universal log fchown() failed"); if (fchmod(private_fd, sbuf.st_mode) == -1) - UNIVERSAL_LOG("fchmod() failed"); + debug(5, L"universal log fchmod() failed"); } /* Linux by default stores the mtime with low precision, low enough that updates that occur in quick succession may @@ -890,7 +890,7 @@ bool env_universal_t::sync(callback_data_list_t *callbacks) /* Apply new file */ success = this->move_new_vars_file_into_place(private_file_path, vars_path); - if (! success) UNIVERSAL_LOG("move_new_vars_file_into_place() failed"); + if (! success) debug(5, L"universal log move_new_vars_file_into_place() failed"); } if (success) @@ -983,7 +983,7 @@ void env_universal_t::parse_message_internal(const wcstring &msgstr, var_table_t { const wchar_t *msg = msgstr.c_str(); - // debug( 3, L"parse_message( %ls );", msg ); + // debug(3, L"parse_message( %ls );", msg); if (msg[0] == L'#') return; @@ -1745,9 +1745,3 @@ static bool bool_from_env_var(const char *name, bool default_value) const char *var = getenv(name); return var ? from_string(var) : default_value; } - -bool universal_log_enabled() -{ - return bool_from_env_var(UNIVERSAL_LOGGING_ENV_NAME, false); -} - diff --git a/src/env_universal_common.h b/src/env_universal_common.h index 60cf00278..edc360167 100644 --- a/src/env_universal_common.h +++ b/src/env_universal_common.h @@ -167,13 +167,7 @@ class universal_notifier_t virtual bool notification_fd_became_readable(int fd); }; -bool universal_log_enabled(); -#define UNIVERSAL_LOG(x) do { if (universal_log_enabled()) fprintf(stderr, "UNIVERSAL LOG: %s\n", x); } while (0) - /* Environment variable for requesting a particular universal notifier. See fetch_default_strategy_from_environment for names. */ #define UNIVERSAL_NOTIFIER_ENV_NAME "fish_universal_notifier" -/* Environment variable for enabling universal variable logging (to stderr) */ -#define UNIVERSAL_LOGGING_ENV_NAME "fish_universal_log" - #endif From 0953590cca7cdc2dab75af5a85c8e2f15d4b8ab2 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Mon, 4 Apr 2016 18:22:48 +0200 Subject: [PATCH 054/363] cd completion: No description for absolute paths This also removes the "pushd/popd" dance and only executes the CDPATH stuff when we need to. --- share/functions/__fish_complete_cd.fish | 46 +++++++++++++++---------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/share/functions/__fish_complete_cd.fish b/share/functions/__fish_complete_cd.fish index ac871d224..cdbd02a78 100644 --- a/share/functions/__fish_complete_cd.fish +++ b/share/functions/__fish_complete_cd.fish @@ -1,24 +1,32 @@ function __fish_complete_cd -d "Completions for the cd command" - set -l cdpath $CDPATH - [ -z "$cdpath" ]; and set cdpath "." - # Remove the real path to "." (i.e. $PWD) from cdpath if we're in it - # so it doesn't get printed in the descriptions - set -l ind - if begin; set ind (contains -i -- $PWD $cdpath) - and contains -- "." $cdpath - end - set -e cdpath[$ind] - end - for i in $cdpath - set -l desc - # Don't show description for current directory - # and replace $HOME with "~" - [ $i = "." ]; or set -l desc (string replace -r -- "^$HOME" "~" "$i") - pushd $i - for d in (commandline -ct)*/ + set -l token (commandline -ct) + # Absolute path - no descriptions and no CDPATH + if string match -q '/*' -- $token + for d in $token*/ # Check if it's accessible - the glob only matches directories - [ -x $d ]; and printf "%s\t%s\n" $d $desc + [ -x $d ]; and printf "%s\n" $d + end + else # Relative path - check $CDPATH and use that as description + set -l cdpath $CDPATH + [ -z "$cdpath" ]; and set cdpath "." + # Remove the real path to "." (i.e. $PWD) from cdpath if we're in it + # so it doesn't get printed in the descriptions + if set -l ind (contains -i -- $PWD $cdpath) + and contains -- "." $cdpath + set -e cdpath[$ind] + end + # TODO: There's a subtlety regarding descriptions - if $cdpath[1]/foo and $cdpath[2]/foo exist, we print both + # but want the first description to win - this currently works, but is not guaranteed + for i in $cdpath + set -l desc + # Don't show description for current directory + # and replace $HOME with "~" + [ $i = "." ]; or set -l desc (string replace -r -- "^$HOME" "~" "$i") + # This assumes the CDPATH component itself is cd-able + for d in $i/$token*/ + # Remove the cdpath component again + [ -x $d ]; and printf "%s\t%s\n" (string replace -r "^$i/" "" -- $d) $desc + end end - popd end end From 47f1a92cc4c3c7056f4172974f10a8dd402cf55c Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Mon, 4 Apr 2016 14:34:28 -0700 Subject: [PATCH 055/363] fixes for cppcheck lint warnings Refine the linting behavior. Fix several of the, mostly trivial, lint errors. --- build_tools/lint.fish | 18 ++++++++++---- src/builtin_string.cpp | 5 ++-- src/common.cpp | 6 ----- src/env_universal_common.cpp | 46 +++++++++++++++++------------------- src/fish_tests.cpp | 6 ++--- src/key_reader.cpp | 4 ++-- src/parse_tree.cpp | 5 ++-- src/parse_util.cpp | 6 +++-- src/wgetopt.cpp | 4 ++-- 9 files changed, 51 insertions(+), 49 deletions(-) diff --git a/build_tools/lint.fish b/build_tools/lint.fish index 3fd90b244..09ab6f462 100755 --- a/build_tools/lint.fish +++ b/build_tools/lint.fish @@ -57,8 +57,14 @@ if set -q c_files[1] echo ======================================== echo Running cppcheck echo ======================================== + # The stderr to stdout redirection is because cppcheck, incorrectly + # IMHO, writes its diagnostic messages to stderr. Anyone running + # this who wants to capture its output will expect those messages to be + # written to stdout. cppcheck -q --verbose --std=posix --std=c11 --language=c++ \ - --inline-suppr --enable=$cppchecks $cppcheck_args $c_files + --template "[{file}:{line}]: {severity} ({id}): {message}" \ + --suppress=missingIncludeSystem \ + --inline-suppr --enable=$cppchecks $cppcheck_args $c_files 2>&1 end if type -q oclint @@ -66,6 +72,10 @@ if set -q c_files[1] echo ======================================== echo Running oclint echo ======================================== + # The stderr to stdout redirection is because oclint, incorrectly + # writes its final summary counts of the errors detected to stderr. + # Anyone running this who wants to capture its output will expect those + # messages to be written to stdout. if test (uname -s) = "Darwin" if not test -f compile_commands.json xcodebuild > xcodebuild.log @@ -73,19 +83,19 @@ if set -q c_files[1] end if test $all = yes oclint-json-compilation-database -e '/pcre2-10.20/' \ - -- -enable-global-analysis + -- -enable-global-analysis 2>&1 else set i_files for f in $c_files set i_files $i_files -i $f end echo oclint-json-compilation-database -e '/pcre2-10.20/' $i_files - oclint-json-compilation-database -e '/pcre2-10.20/' $i_files + oclint-json-compilation-database -e '/pcre2-10.20/' $i_files 2>&1 end else # Presumably we're on Linux or other platform not requiring special # handling for oclint to work. - oclint $c_files -- $argv + oclint $c_files -- $argv 2>&1 end end else diff --git a/src/builtin_string.cpp b/src/builtin_string.cpp index ab864e88d..8911d1ab0 100644 --- a/src/builtin_string.cpp +++ b/src/builtin_string.cpp @@ -794,7 +794,7 @@ public: size_t arglen = wcslen(arg); PCRE2_SIZE bufsize = (arglen == 0) ? 16 : 2 * arglen; wchar_t *output = (wchar_t *)malloc(sizeof(wchar_t) * bufsize); - if (output == 0) + if (output == NULL) { DIE_MEM(); } @@ -820,8 +820,9 @@ public: if (bufsize < MAX_REPLACE_SIZE) { bufsize = std::min(2 * bufsize, MAX_REPLACE_SIZE); + // cppcheck-suppress memleakOnRealloc output = (wchar_t *)realloc(output, sizeof(wchar_t) * bufsize); - if (output == 0) + if (output == NULL) { DIE_MEM(); } diff --git a/src/common.cpp b/src/common.cpp index 84c3de65a..545288fc0 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -1111,12 +1111,6 @@ static void escape_string_internal(const wchar_t *orig_in, size_t in_len, wcstri wcstring escape(const wchar_t *in, escape_flags_t flags) { - if (!in) - { - debug(0, L"%s called with null input", __func__); - FATAL_EXIT(); - } - wcstring result; escape_string_internal(in, wcslen(in), &result, flags); return result; diff --git a/src/env_universal_common.cpp b/src/env_universal_common.cpp index a3815886a..751136159 100644 --- a/src/env_universal_common.cpp +++ b/src/env_universal_common.cpp @@ -663,47 +663,44 @@ bool env_universal_t::load() return success; } -bool env_universal_t::open_temporary_file(const wcstring &directory, wcstring *out_path, int *out_fd) +bool env_universal_t::open_temporary_file(const wcstring &directory, wcstring *out_path, + int *out_fd) { - /* Create and open a temporary file for writing within the given directory */ - /* Try to create a temporary file, up to 10 times. We don't use mkstemps because we want to open it CLO_EXEC. This should almost always succeed on the first try. */ - assert(! string_suffixes_string(L"/", directory)); - + // Create and open a temporary file for writing within the given directory. Try to create a + // temporary file, up to 10 times. We don't use mkstemps because we want to open it CLO_EXEC. + // This should almost always succeed on the first try. + assert(!string_suffixes_string(L"/", directory)); + bool success = false; + int saved_errno; const wcstring tmp_name_template = directory + L"/fishd.tmp.XXXXXX"; wcstring tmp_name; + for (size_t attempt = 0; attempt < 10 && ! success; attempt++) { int result_fd = -1; char *narrow_str = wcs2str(tmp_name_template.c_str()); #if HAVE_MKOSTEMP result_fd = mkostemp(narrow_str, O_CLOEXEC); - if (result_fd >= 0) - { - tmp_name = str2wcstring(narrow_str); - } #else - if (mktemp(narrow_str)) + // cppcheck-suppress redundantAssignment + result_fd = mkstemp(narrow_str); + if (result_fd != -1) { - /* It was successfully templated; try opening it atomically */ - tmp_name = str2wcstring(narrow_str); - result_fd = wopen_cloexec(tmp_name, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0644); + fcntl(result_fd, F_SETFD, O_CLOEXEC); } #endif - - if (result_fd >= 0) - { - /* Success */ - *out_fd = result_fd; - *out_path = str2wcstring(narrow_str); - success = true; - } + + saved_errno = errno; + success = result_fd != -1; + *out_fd = result_fd; + *out_path = str2wcstring(narrow_str); free(narrow_str); } - if (! success) + + if (!success) { - int err = errno; - report_error(err, L"Unable to open file '%ls'", tmp_name.c_str()); + report_error(saved_errno, L"Unable to open file '%ls'", out_path->c_str()); } return success; } @@ -1228,6 +1225,7 @@ class universal_notifier_shmem_poller_t : public universal_notifier_t /* Read the current seed */ this->poll(); + // cppcheck-suppress memleak // addr not really leaked } public: diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 9afb43441..6250513cb 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -458,10 +458,8 @@ static void test_tok() if (types[i] != token.type) { err(L"Tokenization error:"); - wprintf(L"Token number %d of string \n'%ls'\n, got token type %ld\n", - i+1, - str, - (long)token.type); + wprintf(L"Token number %zu of string \n'%ls'\n, got token type %ld\n", + i + 1, str, (long)token.type); } i++; } diff --git a/src/key_reader.cpp b/src/key_reader.cpp index 382f4b229..1cb3b3de4 100644 --- a/src/key_reader.cpp +++ b/src/key_reader.cpp @@ -82,9 +82,9 @@ int main(int argc, char **argv) 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); + sprintf(scratch, "dec: %u hex: %x char: %c\n", c, c, c); else - sprintf(scratch, "dec: %d hex: %x\n", c, c); + sprintf(scratch, "dec: %u hex: %x\n", c, c); writestr(scratch); } /* reset the terminal to the saved mode */ diff --git a/src/parse_tree.cpp b/src/parse_tree.cpp index 5939e270d..51ae77202 100644 --- a/src/parse_tree.cpp +++ b/src/parse_tree.cpp @@ -690,7 +690,7 @@ void parse_ll_t::dump_stack(void) const } } - fprintf(stderr, "Stack dump (%lu elements):\n", symbol_stack.size()); + fprintf(stderr, "Stack dump (%zu elements):\n", symbol_stack.size()); for (size_t idx = 0; idx < lines.size(); idx++) { fprintf(stderr, " %ls\n", lines.at(idx).c_str()); @@ -1685,9 +1685,8 @@ enum parse_bool_statement_type_t parse_node_tree_t::statement_boolean_type(const bool parse_node_tree_t::job_should_be_backgrounded(const parse_node_t &job) const { assert(job.type == symbol_job); - bool result = false; const parse_node_t *opt_background = get_child(job, 2, symbol_optional_background); - result = opt_background != NULL && opt_background->tag == parse_background; + bool result = opt_background != NULL && opt_background->tag == parse_background; return result; } diff --git a/src/parse_util.cpp b/src/parse_util.cpp index 628e86c59..73db28339 100644 --- a/src/parse_util.cpp +++ b/src/parse_util.cpp @@ -1020,14 +1020,16 @@ static const wchar_t *error_format_for_character(wchar_t wc) void parse_util_expand_variable_error(const wcstring &token, size_t global_token_pos, size_t dollar_pos, parse_error_list_t *errors) { - // Note that dollar_pos is probably VARIABLE_EXPAND or VARIABLE_EXPAND_SINGLE, not a literal dollar sign + // Note that dollar_pos is probably VARIABLE_EXPAND or VARIABLE_EXPAND_SINGLE, + // not a literal dollar sign. assert(errors != NULL); assert(dollar_pos < token.size()); const bool double_quotes = (token.at(dollar_pos) == VARIABLE_EXPAND_SINGLE); const size_t start_error_count = errors->size(); const size_t global_dollar_pos = global_token_pos + dollar_pos; const size_t global_after_dollar_pos = global_dollar_pos + 1; - wchar_t char_after_dollar = (dollar_pos + 1 >= token.size() ? L'\0' : token.at(dollar_pos + 1)); + wchar_t char_after_dollar = dollar_pos + 1 >= token.size() ? 0 : token.at(dollar_pos + 1); + switch (char_after_dollar) { case BRACKET_BEGIN: diff --git a/src/wgetopt.cpp b/src/wgetopt.cpp index c2905ab0a..7951eaea0 100644 --- a/src/wgetopt.cpp +++ b/src/wgetopt.cpp @@ -515,7 +515,7 @@ int wgetopter_t::_wgetopt_internal(int argc, wchar_t **argv, const wchar_t *opts { if (wopterr) { - fwprintf(stderr, _(L"%ls: Invalid option -- %lc\n"), argv[0], c); + fwprintf(stderr, _(L"%ls: Invalid option -- %lc\n"), argv[0], (wint_t)c); } woptopt = c; @@ -554,7 +554,7 @@ int wgetopter_t::_wgetopt_internal(int argc, wchar_t **argv, const wchar_t *opts { /* 1003.2 specifies the format of this message. */ fwprintf(stderr, _(L"%ls: Option requires an argument -- %lc\n"), - argv[0], c); + argv[0], (wint_t)c); } woptopt = c; if (optstring[0] == ':') From cb6d5d76c8232264f66c9a1db895d9dd69d77bc3 Mon Sep 17 00:00:00 2001 From: Mark Griffiths Date: Mon, 4 Apr 2016 13:43:37 +0100 Subject: [PATCH 056/363] update lexicon for latest docs Closes #2699 Fixes issues with: * 'string' function synopsis * Redirection display issues * Better file & path detection * Rendering of % & @ chars in both html and man * @ symbol in tutorial Improves robustness by implementing an @EOL marker to prevent hold buffer dumping extra chars after the end of an expression. Added new '{{' and '}}' meta-chars for when you want curly braces in a regexp that was previously tripping up the lexicon. Improve man/html presentation consistency for * string * printf * prompt_pwd * type Use cli-styling for 'practical' examples. Add tag for presenting content with preceding backslash. Signed-off-by: Mark Griffiths --- Doxyfile | 2 + Doxyfile.help | 2 + Doxyfile.user | 2 + Makefile.in | 8 +- doc_src/FORMATTING.md | 2 + doc_src/fish_right_prompt.txt | 2 +- doc_src/index.hdr.in | 28 +++-- doc_src/printf.txt | 2 +- doc_src/prompt_pwd.txt | 13 +- doc_src/string.txt | 226 +++++++++++++++------------------- doc_src/type.txt | 6 +- doc_src/user_doc.css | 2 +- lexicon_filter.in | 56 ++++++--- 13 files changed, 180 insertions(+), 171 deletions(-) diff --git a/Doxyfile b/Doxyfile index d12875a58..cf025f1b1 100644 --- a/Doxyfile +++ b/Doxyfile @@ -274,6 +274,8 @@ ALIASES += "span{2}=\2" ALIASES += "spcl{2}=\2" ALIASES += "bksl{1}=\\\1" +ALIASES += "pcnt{1}=\%\1" +ALIASES += "atat{1}=\@" # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" diff --git a/Doxyfile.help b/Doxyfile.help index 979074144..ec4a97c44 100644 --- a/Doxyfile.help +++ b/Doxyfile.help @@ -274,6 +274,8 @@ ALIASES += "span{2}=\2" ALIASES += "spcl{2}=\2" ALIASES += "bksl{1}=\\\1" +ALIASES += "pcnt{1}=\%\1" +ALIASES += "atat{1}=\@" # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" diff --git a/Doxyfile.user b/Doxyfile.user index 96c3bc228..ea2752465 100644 --- a/Doxyfile.user +++ b/Doxyfile.user @@ -274,6 +274,8 @@ ALIASES += "span{2}=\2" ALIASES += "spcl{2}=\2" ALIASES += "bksl{1}=\\1" +ALIASES += "pcnt{1}=%\1" +ALIASES += "atat{1}=@" # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" diff --git a/Makefile.in b/Makefile.in index 98b8a11ca..ede69b0fd 100644 --- a/Makefile.in +++ b/Makefile.in @@ -103,7 +103,7 @@ FISH_OBJS := obj/function.o obj/builtin.o obj/complete.o obj/env.o obj/exec.o \ obj/parse_productions.o obj/parse_execution.o obj/pager.o obj/utf8.o \ obj/fish_version.o obj/wcstringutil.o -FISH_INDENT_OBJS := obj/fish_indent.o obj/print_help.o $(FISH_OBJS) +FISH_INDENT_OBJS := obj/fish_indent.o obj/print_help.o $(FISH_OBJS) # # Additional files used by builtin.o @@ -276,9 +276,9 @@ doc: $(HDR_FILES_SRC) Doxyfile.user $(HTML_SRC) $(HELP_SRC) doc.h \ (cat Doxyfile.user; echo INPUT_FILTER=./lexicon_filter; \ echo PROJECT_NUMBER=$(FISH_BUILD_VERSION) | $(SED) "s/-.*//") | \ doxygen - && touch user_doc; \ - cd user_doc/html && rm -f bc_s.png bdwn.png closed.png ftv2*.png \ - nav*.png open.png sync_*.png tab*.* doxygen.* dynsections.js \ - jquery.js pages.html + cd user_doc/html && rm -f arrow*.png bc_s.png bdwn.png closed.png \ + doc.png folder*.png ftv2*.png nav*.png open.png splitbar.png \ + sync_*.png tab*.* doxygen.* dynsections.js jquery.js pages.html # # PDF version of the source code documentation. diff --git a/doc_src/FORMATTING.md b/doc_src/FORMATTING.md index 0ae1c81e1..df6de6f8d 100644 --- a/doc_src/FORMATTING.md +++ b/doc_src/FORMATTING.md @@ -160,6 +160,8 @@ The following can be used in \\fish blocks to render some fish scenarios. These - ``: \This would be shown as an error.\ - ``: \This test will not be parsed for fish markup.\ - ``: \This would be rendered as command/script output.\ +- ``: Render the contents with a preceding backslash. Useful when presenting output. +- `{{` and `}}`: Required when wanting curly braces in regular expression example. ### Prompts and cursors diff --git a/doc_src/fish_right_prompt.txt b/doc_src/fish_right_prompt.txt index 21b4a1954..87de31139 100644 --- a/doc_src/fish_right_prompt.txt +++ b/doc_src/fish_right_prompt.txt @@ -20,7 +20,7 @@ A simple right prompt: \fish function fish_right_prompt -d "Write out the right prompt" - date "+%m/%d/%y" + date '+%m/%d/%y' end \endfish diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index 15c67c379..a42fb6498 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -528,22 +528,24 @@ The above code demonstrates how to use multiple '`$`' symbols to expand the valu Lists adjacent to other lists or strings are expanded as cartesian products: Examples: -\fish -echo {good,bad}" apples" -# Outputs 'good apples bad apples' +\fish{cli-dark} +>_ echo {good,bad}" apples" +good apples bad apples -set -l a x y z -set -l b 1 2 3 -echo $a$b -# Outputs 'x1 y1 z1 x2 y2 z2 x3 y3 z3' -echo $a"-"$b -# Outputs 'x-1 y-1 z-1 x-2 y-2 z-2 x-3 y-3 z-3' +>_ set -l a x y z +>_ set -l b 1 2 3 -echo {x,y,z}$b -# Outputs 'x1 y1 z1 x2 y2 z2 x3 y3 z3' +>_ echo $a$b +x1 y1 z1 x2 y2 z2 x3 y3 z3 -echo {$b}word -# Outputs '1word 2word 3word' +>_ echo $a"-"$b +x-1 y-1 z-1 x-2 y-2 z-2 x-3 y-3 z-3 + +>_ echo {x,y,z}$b +x1 y1 z1 x2 y2 z2 x3 y3 z3 + +>_ echo {$b}word +1word 2word 3word \endfish Be careful when you try to use braces to separate variable names from text. The dangers noted in the last example above can be avoided by wrapping the variable in double quotes instead of braces (`echo "$b"word`). diff --git a/doc_src/printf.txt b/doc_src/printf.txt index ce7796c85..5e22db986 100644 --- a/doc_src/printf.txt +++ b/doc_src/printf.txt @@ -60,7 +60,7 @@ This file has been imported from the printf in GNU Coreutils version 6.9. If you \subsection printf-example Example \fish -printf '\%s\\t\%s\n' flounder fish +printf '%s\\t%s\\n' flounder fish \endfish Will print "flounder fish" (separated with a tab character), followed by a newline character. This is useful for writing completions, as fish expects completion scripts to output the option followed by the description, separated with a tab character. diff --git a/doc_src/prompt_pwd.txt b/doc_src/prompt_pwd.txt index 7161d8aa9..0eafbf994 100644 --- a/doc_src/prompt_pwd.txt +++ b/doc_src/prompt_pwd.txt @@ -13,16 +13,19 @@ To change the number of characters per path component, set $fish_prompt_pwd_dir_ \subsection prompt_pwd-example Examples -\fish +\fish{cli-dark} >_ cd ~/ >_ echo $PWD -/home/alfa +/home/alfa + >_ prompt_pwd -~ +~ + >_ cd /tmp/banana/sausage/with/mustard >_ prompt_pwd -/t/b/s/w/mustard +/t/b/s/w/mustard + >_ set -g fish_prompt_pwd_dir_length 3 >_ prompt_pwd -/tmp/ban/sau/wit/mustard +/tmp/ban/sau/wit/mustard \endfish diff --git a/doc_src/string.txt b/doc_src/string.txt index 37d244353..fd77da945 100644 --- a/doc_src/string.txt +++ b/doc_src/string.txt @@ -3,18 +3,13 @@ \subsection string-synopsis Synopsis \fish{synopsis} string length [(-q | --quiet)] [STRING...] -string sub [(-s | --start) START] [(-l | --length) LENGTH] - [(-q | --quiet)] [STRING...] -string split [(-m | --max) MAX] [(-r | --right)] [(-q | --quiet)] - SEP [STRING...] +string sub [(-s | --start) START] [(-l | --length) LENGTH] [(-q | --quiet)] [STRING...] +string split [(-m | --max) MAX] [(-r | --right)] [(-q | --quiet)] SEP [STRING...] string join [(-q | --quiet)] SEP [STRING...] -string trim [(-l | --left)] [(-r | --right)] [(-c | --chars CHARS)] - [(-q | --quiet)] [STRING...] +string trim [(-l | --left)] [(-r | --right)] [(-c | --chars CHARS)] [(-q | --quiet)] [STRING...] string escape [(-n | --no-quoted)] [STRING...] -string match [(-a | --all)] [(-i | --ignore-case)] [(-r | --regex)] - [(-n | --index)] [(-q | --quiet)] PATTERN [STRING...] -string replace [(-a | --all)] [(-i | --ignore-case)] [(-r | --regex)] - [(-q | --quiet)] PATTERN REPLACEMENT [STRING...] +string match [(-a | --all)] [(-i | --ignore-case)] [(-r | --regex)] [(-n | --index)] [(-q | --quiet)] PATTERN [STRING...] +string replace [(-a | --all)] [(-i | --ignore-case)] [(-r | --regex)] [(-q | --quiet)] PATTERN REPLACEMENT [STRING...] \endfish @@ -51,150 +46,129 @@ The following subcommands are available: \subsection string-example Examples -\fish -string length 'hello, world' -# Output: -# 12 +\fish{cli-dark} +>_ string length 'hello, world' +12 -string length -q $str +>_ set str foo +>_ string length -q $str; echo $status +1 # Equivalent to test -n $str \endfish -\fish -string sub --length 2 abcde -# Output: -# ab +\fish{cli-dark} +>_ string sub --length 2 abcde +ab -string sub -s 2 -l 2 abcde -# Output: -# bc +>_ string sub -s 2 -l 2 abcde +bc -string sub --start=-2 abcde -# Output: -# de +>_ string sub --start=-2 abcde +de \endfish -\fish -string split . example.com -# Output: -# example -# com +\fish{cli-dark} +>_ string split . example.com +example +com -string split -r -m1 / /usr/local/bin/fish -# Output: -# /usr/local/bin -# fish +>_ string split -r -m1 / /usr/local/bin/fish +/usr/local/bin +fish -string split '' abc -# Output: -# a -# b -# c +>_ string split '' abc +a +b +c \endfish -\fish -seq 3 | string join ... -# Output: -# 1...2...3 +\fish{cli-dark} +>_ seq 3 | string join ... +1...2...3 \endfish -\fish -string trim ' abc ' -# Output: -# abc +\fish{cli-dark} +>_ string trim ' abc ' +abc -string trim --right --chars=yz xyzzy zany -# Output: -# x -# zan +>_ string trim --right --chars=yz xyzzy zany +x +zan \endfish -\fish -echo \x07 | string escape -# Output: -# \\cg +\fish{cli-dark} +>_ echo \\x07 | string escape +cg \endfish -\fish -# string match glob examples +\subsection string-example-match-glob Match Glob Examples -string match '?' a -# Output: -# a +\fish{cli-dark} +>_ string match '?' a +a -string match 'a*b' axxb -# Output: -# axxb +>_ string match 'a*b' axxb +axxb -string match -i 'a??B' Axxb -# Output: -# Axxb +>_ string match -i 'a??B' Axxb +Axxb -echo 'ok?' | string match '*\\?' -# Output: -# ok? - -# string match regex examples - -string match -r 'cat|dog|fish' 'nice dog' -# Output: -# dog - -string match -r '(\\d\\d?):(\\d\\d):(\\d\\d)' 2:34:56 -# Output: -# 2:34:56 -# 2 -# 34 -# 56 - -string match -r '^(\\w{2,4})\\g1$' papa mud murmur -# Output: -# papa -# pa -# murmur -# mur - -string match -r -a -n at ratatat -# Output: -# 2 2 -# 4 2 -# 6 2 - -string match -r -i '0x[0-9a-f]{1,8}' 'int magic = 0xBadC0de;' -# Output: -# 0xBadC0de +>_ echo 'ok?' | string match '*\\?' +>_ ok? \endfish -\fish +\subsection string-example-match-regex Match Regex Examples -# string replace literal examples +\fish{cli-dark} +>_ string match -r 'cat|dog|fish' 'nice dog' +dog -string replace is was 'blue is my favorite' -# Output: -# blue was my favorite +>_ string match -r '(\\d\\d?):(\\d\\d):(\\d\\d)' 2:34:56 +2:34:56 +2 +34 +56 -string replace 3rd last 1st 2nd 3rd -# Output: -# 1st -# 2nd -# last +>_ string match -r '^(\\w{{2,4}})\\g1$' papa mud murmur +papa +pa +murmur +mur -string replace -a ' ' _ 'spaces to underscores' -# Output: -# spaces_to_underscores +>_ string match -r -a -n at ratatat +2 2 +4 2 +6 2 -# string replace regex examples - -string replace -r -a '[^\\d.]+' ' ' '0 one two 3.14 four 5x' -# Output: -# 0 3.14 5 - -string replace -r '(\\w+)\\s+(\\w+)' '$2 $1 $$' 'left right' -# Output: -# right left $ - -string replace -r '\\s*newline\\s*' '\n' 'put a newline here' -# Output: -# put a -# here +>_ string match -r -i '0x[0-9a-f]{{1,8}}' 'int magic = 0xBadC0de;' +0xBadC0de +\endfish + +\subsection string-example-replace-literal Replace Literal Examples + +\fish{cli-dark} +>_ string replace is was 'blue is my favorite' +blue was my favorite + +>_ string replace 3rd last 1st 2nd 3rd +1st +2nd +last + +>_ string replace -a ' ' _ 'spaces to underscores' +spaces_to_underscores +\endfish + +\subsection string-example-replace-Regex Replace Regex Examples + +\fish{cli-dark} +>_ string replace -r -a '[^\\d.]+' ' ' '0 one two 3.14 four 5x' +0 3.14 5 + +>_ string replace -r '(\\w+)\\s+(\\w+)' '$2 $1 $$' 'left right' +right left $ + +>_ string replace -r '\\s*newline\\s*' '\\n' 'put a newline here' +put a +here \endfish diff --git a/doc_src/type.txt b/doc_src/type.txt index 2f55d72ab..2da88c94d 100644 --- a/doc_src/type.txt +++ b/doc_src/type.txt @@ -26,7 +26,7 @@ The following options are available: \subsection type-example Example -\fish -type fg -# Outputs the string 'fg is a shell builtin'. +\fish{cli-dark} +>_ type fg +fg is a builtin \endfish diff --git a/doc_src/user_doc.css b/doc_src/user_doc.css index ba1362e22..2ac484ca0 100644 --- a/doc_src/user_doc.css +++ b/doc_src/user_doc.css @@ -174,8 +174,8 @@ tt, code, pre, .fish { .comment, .suggest { color: #555; } .command, .function, .binary { color: #223aa4; } .argument, .path, .file { color: #5961cf; } -.string { color: #6c6d08; } .operator, .variable, .match, .history { color: #1c8885; } +.string, .string .operator { color: #858904; } /* Synopsis variant */ .synopsis { diff --git a/lexicon_filter.in b/lexicon_filter.in index 7583cc4ac..e9555e8f2 100644 --- a/lexicon_filter.in +++ b/lexicon_filter.in @@ -46,7 +46,12 @@ # Then if it's inline. Remove and process immediately... /^\\fish.*$/ { # Catch @ symbol - s/@/(at)/ + s/@/@at/g + # Catch & symbol + s/&\([^a-z]\)/@amp\1/g + # Catch {{ & }} symbols + s/{{/@curlyL/g + s/}}/@curlyR/g s/^\\fish// s/\\endfish// b html @@ -56,7 +61,12 @@ # Inside \fish block. Process... /\\endfish/!{ # Catch @ symbol - s/@/((d))/ + s/@/@at/g + # Catch & symbol + s/&\([^a-z]\)/@amp\1/g + # Catch {{ & }} symbols + s/{{/@curlyL/g + s/}}/@curlyR/g # Preprocess HTML and HTML-like formatting /<[^>]*>/ { b html @@ -103,6 +113,9 @@ s||}| s||@undr{| s|]*>|@undr{| s||}| +# Backslash (when escaping output) +s||@bksl{| +s||}| t html #. # Some handy non-standard extensions @@ -224,11 +237,11 @@ s/ \\$/ @bksl{ }/ #. # Normal Directory s|mkdir |mkdir :| -s|\([~/:][/]*[.A-Za-z_0-9/-]*\)\\ |\1=|g -s| \([~/][/]*[.A-Za-z_0-9/=-]*\)| \\\ +s|\([~/:][/]*[.A-Za-z_0-9*/-]*\)\\ |\1=|g +s| \([~/][/]*[.A-Za-z_0-9*/=-]*\)| \\\ <@path{\1}\ |g -s| \(:[/]*[.A-Za-z_0-9/=-]*\)| \\\ +s| \(:[/]*[.A-Za-z_0-9*/=-]*\)| \\\ <@path{\1}\ |g t protect @@ -255,6 +268,7 @@ s|^\([a-zA-Z][{},a-zA-Z0-9%*._/?!-]*\)|@args{\1}|g # Pick up loose text after markup. s/\([})]\)\([a-zA-Z0-9+%*.,][,a-zA-Z0-9%*._/?!-]*\);/\1@args{\2};/g s/\([})]\)\([a-zA-Z0-9+%*.,][,a-zA-Z0-9%*._/?!-]*\)$/\1@args{\2}/g +s/\([})]\)\([a-zA-Z0-9+%*.,][,a-zA-Z0-9%*._/?!-]*\)@EOL/\1@args{\2}/g #. # Uncomment the following 2 lines (ss) to log the pattern buffer. s/^.*$/Pattern : &/w lexicon.log @@ -279,6 +293,8 @@ s,\([^\\ ]*\)\\\n\([^<]*\)<\(@[^}]*[}\\]\),\1\3\2, t join # Clean up stray new lines s/\n//g +# Clean up past @EOL +s/@EOL.*$//g #. # Uncomment the folowing two lines (ss) to log the buffer before 'cleaning'. s/^.*$/PreClean: &/w lexicon.log @@ -475,16 +491,17 @@ x #. # Mark up sesitive character entities. #. -# We comment out this target because it isn't referenced and if we don't we -# get warnings about "unused label 'entities'". -#:entities s//\>/g -s/((d))/@/g +s/@amp/\&/g +s/@curlyL/\{/g +s/@curlyR/\}/g +s/@at/@atat{ }/g #. # Final post processing s/};\([^]]\)/}@redr{;}\1/g s/};$/}@redr{;}/ +s/@sglq{}/''/ s/ \[\([@(]\)/ @args{[}\1/g s/ \[\([A-Z]*\) / @args{[\1} /g s/@args{\([a-zA-Z0-9_.]*\)}\]/@args{\1]}/g @@ -496,7 +513,9 @@ s/ \]$/ @args{]}/g s/\]}\]$/]]}/ s/\\\([()]\)/@optr{@bksl{\1}}/g s/\([()]\)/@optr{\1}/g +s/\\\\\([cdgnstwx?]\)/@bksl{\1}/g s/\\n/@bksl{n}/ +s/%\([diouxXfgGeEsbmy]\)/@pcnt{\1}/g s/ \\$// #. # Uncomment the folowing two lines (ss) to log the final output, sent to Doxygen. @@ -516,6 +535,8 @@ b #. # Move protected content to hold space and mark up other entities. :protect +# Add an 'End of Line' marker +s/$/@EOL/ s/^.*$/Input : &/w lexicon.log s/^Input : // h @@ -536,6 +557,7 @@ x s/[^\<]*// s/^ *\\\n//g s/\n *\\//g + s/\n@EOL//g s/[()] \\//g s/^[^\<][^@][^\\]*// s/\n[]|;) ][^\\]*\\// @@ -559,11 +581,13 @@ s/^[a-z][a-z]* \n// # Swap the buffers back. x #. -# A special case. Tidy up after commands. +# A special case. Tidy up after performing command substitution. # Redirectors s/\([^{|] *\)|/\1@redr{|}/g -s/&$/@redr{\&}/ -s/\([^{&] *\)&[^a-z]/\1@redr{\&}/g +s/\&@EOL$/@redr{@amp}@EOL/g +s/@amp@EOL$/@redr{@amp}@EOL/g +s/\([<>]\)@amp\([0-9]\)/@redr{\1@amp\2}/g +s/\([^{&] *\)&[^@a-z]/\1@redr{\&}/g s/\([^{<>^] *\)\([0-9]* *[<>^][<>^]*[^@][a-zA-Z0-9./_-]*\)/\1@redr{\2}/g s/\\}/}\\/g #. @@ -575,11 +599,9 @@ s/[[][0-9$a-zA-Z_;. -]*]/@args{&}/g s/\($[$]*\)\([A-Za-z_0-9][A-Za-z_0-9]*\)/@vars{@optr{\1}\2}/g #. # Files -s/\([^@]\)\([A-Za-z0-9_-][A-Za-z0-9_-]*\.[a-z0-9*][a-z0-9*]*\)/\1@fsfo{\2}/g -#. -# We comment out this target because it isn't referenced and if we don't we -# get warnings about "unused label 'commands'". -#:commands +/@at/ ! { + s/\([A-Za-z0-9_*-][A-Za-z0-9_*-]*\.[a-z0-9*][a-z0-9*]*\)/@fsfo{\1}/g +} #. #### This section is built in the Makefile. Just some formatting examples. ##### #. From 484c1484c9ae01354b1910bae5ed8a729e00b3be Mon Sep 17 00:00:00 2001 From: David Adam Date: Mon, 4 Apr 2016 14:33:35 +0800 Subject: [PATCH 057/363] Customisable extra configuration, completion and function directories - Add options to the autotools build to set the path for the "vendor" or "extra" configuration snippets, functions and completions directories. - Remove the vendor_completions directory from the Xcode build, as these are relocatable and compiling the paths in does not make sense. This allows packaging tools like Homebrew and Nix to use a common directory outside of the main prefix for third-party completions, and to make these available for programmatic discovery through `pkg-config`. Closes #2113 --- .gitignore | 1 + Makefile.in | 17 ++++++++--- configure.ac | 26 ++++++++++++++-- doc_src/index.hdr.in | 52 +++++++++++++++++++++++++++----- fish.pc.in | 6 ++-- fish.xcodeproj/project.pbxproj | 28 ----------------- share/__fish_build_paths.fish.in | 8 +++++ share/config.fish | 17 ++++++++--- 8 files changed, 106 insertions(+), 49 deletions(-) create mode 100644 share/__fish_build_paths.fish.in diff --git a/.gitignore b/.gitignore index 704ec8d01..e0713b06e 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ fish_tests fish.pc seq set_color +share/__fish_build_paths.fish share/man/ toc.txt user_doc/ diff --git a/Makefile.in b/Makefile.in index ede69b0fd..5526fa6db 100644 --- a/Makefile.in +++ b/Makefile.in @@ -53,6 +53,9 @@ mandir = @mandir@ sysconfdir = @sysconfdir@ docdir = @docdir@ localedir = @localedir@ +extra_completionsdir = @extra_completionsdir@ +extra_functionsdir = @extra_functionsdir@ +extra_snippetsdir = @extra_snippetsdir@ # # pcre2 @@ -224,7 +227,7 @@ endif # Make everything needed for installing fish # -all: $(PROGRAMS) $(user_doc) $(share_man) $(TRANSLATIONS) fish.pc +all: $(PROGRAMS) $(user_doc) $(share_man) $(TRANSLATIONS) fish.pc share/__fish_build_paths.fish @echo fish has now been built. @echo Use \'$(MAKE) install\' to install fish. .PHONY: all @@ -467,6 +470,9 @@ doc.h: $(HDR_FILES) -e "s,@sysconfdir\@,$(sysconfdir),g" \ -e "s,@datadir\@,$(datadir),g" \ -e "s,@docdir\@,$(docdir),g" \ + -e "s,@extra_completionsdir\@,$(extra_completionsdir),g" \ + -e "s,@extra_functionsdir\@,$(extra_functionsdir),g" \ + -e "s,@extra_snippetsdir\@,$(extra_snippetsdir),g" \ -e "s|@configure_input\@|$@, generated from $@.in by the Makefile. DO NOT MANUALLY EDIT THIS FILE!|g" \ -e "s,@prefix\@,$(prefix),g" \ -e "s,@fish_build_version\@,$(FISH_BUILD_VERSION),g" \ @@ -657,9 +663,9 @@ install-force: all install-translations $(INSTALL) -m 755 -d $(DESTDIR)$(sysconfdir)/fish/conf.d $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/completions - $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/vendor_completions.d - $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/vendor_functions.d - $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/vendor_conf.d + $(INSTALL) -m 755 -d $(DESTDIR)$(extra_completionsdir) + $(INSTALL) -m 755 -d $(DESTDIR)$(extra_functionsdir) + $(INSTALL) -m 755 -d $(DESTDIR)$(extra_snippetsdir) $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/functions $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/man/man1 $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/tools @@ -669,6 +675,7 @@ install-force: all install-translations $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/tools/web_config/sample_prompts $(INSTALL) -m 644 etc/config.fish $(DESTDIR)$(sysconfdir)/fish/ $(INSTALL) -m 644 share/config.fish $(DESTDIR)$(datadir)/fish/ + $(INSTALL) -m 644 share/__fish_build_paths.fish $(DESTDIR)$(datadir)/fish/ $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/pkgconfig $(INSTALL) -m 644 fish.pc $(DESTDIR)$(datadir)/pkgconfig for i in $(COMPLETIONS_DIR_FILES:%='%'); do \ @@ -899,7 +906,7 @@ clean: rm -f doc_src/index.hdr doc_src/commands.hdr rm -f lexicon_filter lexicon.txt lexicon.log rm -f compile_commands.json xcodebuild.log - rm -f FISH-BUILD-VERSION-FILE fish.pc + rm -f FISH-BUILD-VERSION-FILE fish.pc share/__fish_build_paths.fish if test "$(HAVE_DOXYGEN)" = 1; then \ rm -rf doc user_doc share/man; \ fi diff --git a/configure.ac b/configure.ac index af4fc5aff..f31825ce8 100644 --- a/configure.ac +++ b/configure.ac @@ -845,7 +845,7 @@ You may need to install the PCRE2 development library for your system.]) fi fi -# Re-test as value may have changed +# Re-test as value may have changed. if test "x$included_pcre2" = "xyes"; then # Build configure/Makefile for pcre2 AC_MSG_NOTICE([using included PCRE2 library]) @@ -862,7 +862,29 @@ if test "x$included_pcre2" = "xyes"; then LIBS="$LIBS $PCRE2_LIBS" fi -# Tell the world what we know +# Allow configurable extra directories. +AC_SUBST(extra_completionsdir) +AC_ARG_WITH([extra-completionsdir], + AS_HELP_STRING([--with-extra-completionsdir=DIR], + [path for extra completions]), + [extra_completionsdir=$withval], + [extra_completionsdir='${datadir}/fish/vendor_completions.d']) + +AC_SUBST(extra_functionsdir) +AC_ARG_WITH([extra_functionsdir], + AS_HELP_STRING([--with-extra-functionsdir=DIR], + [path for extra functions]), + [extra_functionsdir=$withval], + [extra_functionsdir='${datadir}/fish/vendor_functions.d']) + +AC_SUBST(extra_snippetsdir) +AC_ARG_WITH([extra-snippetsdir], + AS_HELP_STRING([--with-extra-snippetsdir=DIR], + [path for extra snippets]), + [extra_snippetsdir=$withval], + [extra_snippetsdir='${datadir}/fish/vendor_conf.d']) + +# Tell the world what we know. AC_CONFIG_FILES([Makefile]) AC_OUTPUT diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index a42fb6498..b75ee5e1d 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -247,11 +247,21 @@ Functions can be defined on the commandline or in a configuration file, but they Fish automatically searches through any directories in the array variable `$fish_function_path`, and any functions defined are automatically loaded when needed. A function definition file must have a filename consisting of the name of the function plus the suffix '`.fish`'. -The default value for `$fish_function_path` is `~/.config/fish/functions` `/etc/fish/functions` `/usr/share/fish/functions`. The exact path to the last two of these may be slightly different depending on what install path prefix was chosen at configuration time. The rationale behind having three different directories is that the first one is for user specific functions, the second one is for system-wide additional functions and the last one is for default fish functions. The path list is searched in order, meaning that by default, the system administrator can override default fish functions, and the user can override functions defined by the system administrator. +By default, Fish searches the following for functions, using the first available file that it finds: +- A directory for end-users to keep their own functions, usually `~/.config/fish/functions` (controlled by the `XDG_CONFIG_HOME` environment variable). +- A directory for systems administrators to install functions for all users on the system, usually `/etc/fish/functions`. +- A directory for third-party software vendors to ship their own functions for their software, usually `/usr/share/fish/vendor_functions.d`. +- The functions shipped with fish, usually installed in `/usr/share/fish/functions`. + +These paths are controlled by parameters set at build, install, or run time, and may vary from the defaults listed above. + +This wide search may be confusing. If you are unsure, your functions probably belong in `~/.config/fish/functions`. It is very important that function definition files only contain the definition for the specified function and nothing else. Otherwise, it is possible that autoloading a function files requires that the function already be loaded, which creates a circular dependency. -Autoloading also won't work for event handlers, since fish cannot know that a function is supposed to be executed when an event occurs when it hasn't yet loaded the function. See there for details. +Autoloading also won't work for event handlers, since fish cannot know that a function is supposed to be executed when an event occurs when it hasn't yet loaded the function. See the event handlers section for more information. + +If you are developing another program, you may wish to install functions which are available for all users of the fish shell on a system. They can be installed to the "vendor" functions directory. As this path may vary from system to system, the `pkgconfig` framework should be used to discover this path with the output of `pkg-config --variable functionsdir fish`. \subsubsection syntax-conditional Conditional execution of code and flow control @@ -381,9 +391,20 @@ Functions beginning with the string `__fish_print_` print a newline separated li Completions can be defined on the commandline or in a configuration file, but they can also be automatically loaded. Fish automatically searches through any directories in the array variable `$fish_complete_path`, and any completions defined are automatically loaded when needed. A completion file must have a filename consisting of the name of the command to complete and the suffix '`.fish`'. -The default value for `$fish_complete_path` is `~/.config/fish/completions` `/etc/fish/completions` `/usr/share/fish/vendor_completions.d` `/usr/share/fish/completions` `~/.local/share/generated_completions`. (Some paths may be slightly different depending on where fish is installed). If a suitable file is found in one of these directories, it will be automatically loaded and the search will be stopped. The large number of directories searched may be confusing. It is to allow, respectively, user-specific completions, system-wide completions, completions installed by other packages, default completions that ship with fish, and finally, completions generated from manual pages. If you are unsure, put your completions in `~/.config/fish/completions`. +By default, Fish searches the following for completions, using the first available file that it finds: +- A directory for end-users to keep their own completions, usually `~/.config/fish/completions` (controlled by the `XDG_CONFIG_HOME` environment variable); +- A directory for systems administrators to install completions for all users on the system, usually `/etc/fish/completions`; +- A directory for third-party software vendors to ship their own completions for their software, usually `/usr/share/fish/vendor_completions.d`; +- The completions shipped with fish, usually installed in `/usr/share/fish/completions`; and +- Completions automatically generated from the operating system's manual, usually stored in `~/.local/share/generated_completions`. -If you have written new completions for a common Unix command, please consider sharing your work by submitting it via the instructions in Further help and development. If you are developing another program and would like to ship completions with your program, install them to `/usr/share/fish/vendor_completions.d` or similar directory. Systems using the `pkgconfig` framework can discover this path from the output of `pkg-config --variable completionsdir fish`. +These paths are controlled by parameters set at build, install, or run time, and may vary from the defaults listed above. + +This wide search may be confusing. If you are unsure, your completions probably belong in `~/.config/fish/completions`. + +If you have written new completions for a common Unix command, please consider sharing your work by submitting it via the instructions in Further help and development. + +If you are developing another program and would like to ship completions with your program, install them to the "vendor" completions directory. As this path may vary from system to system, the `pkgconfig` framework should be used to discover this path with the output of `pkg-config --variable completionsdir fish`. \section expand Parameter expansion (Globbing) @@ -1085,9 +1106,27 @@ Note that functions cannot be started in the background. Functions that are stop \section initialization Initialization files -On startup, `fish` evaluates the files `/usr/share/fish/config.fish` (Or `/usr/local/fish...` if you installed fish in `/usr/local`), `/etc/fish/config.fish` (Or `~/etc/fish/...` if you installed fish in your home directory) and `~/.config/fish/config.fish` (Or any other directory specified by the `$XDG_CONFIG_HOME` variable), in that order. +On startup, Fish evaluates a number of configuration files, which can be used to control the behavior of the shell. -The first file should not be directly edited, the second one is meant for systemwide configuration and the last one is meant for user configuration. If you want to run a command only on starting an interactive shell, use the exit status of the command `status --is-interactive` to determine if the shell is interactive. If you want to run a command only when using a login shell, use `status --is-login` instead. +Configuration files are evaluated in the following order: +- Configuration shipped with fish, which should not be edited, usually `/usr/share/fish/config.fish`. +- System-wide configuration files, where administrators can include initialization that should be run for all users on the system - similar to `/etc/profile` for POSIX-style shells - usually `/etc/fish/config.fish`; +- "Unit" configuration files ending in `.fish`, in the directories: + - `~/.config/fish/conf.d/` + - `/etc/fish/conf.d` + - `/usr/share/fish/vendor_conf.d` + + If there are multiple files with the same name in these directories, only the first will be executed. + +- User initialization, usually in `~/.config/fish/config.fish` (controlled by the `XDG_CONFIG_HOME` environment variable). + +These paths are controlled by parameters set at build, install, or run time, and may vary from the defaults listed above. + +This wide search may be confusing. If you are unsure, use `~/.config/fish/config.fish`. + +These files are all executed on the startup of every shell. If you want to run a command only on starting an interactive shell, use the exit status of the command `status --is-interactive` to determine if the shell is interactive. If you want to run a command only when using a login shell, use `status --is-login` instead. This will speed up the starting of non-interactive or non-login shells. + +If you are developing another program, you may wish to install configuration which is run for all users of the fish shell on a system. This is discouraged; if not carefully written, they may have side-effects or slow the startup of the shell. Additionally, users of other shells will not benefit from the Fish-specific configuration. However, if they are absolutely required, you may install them to the "vendor" configuration directory. As this path may vary from system to system, the `pkgconfig` framework should be used to discover this path with the output of `pkg-config --variable confdir fish`. Examples: @@ -1107,7 +1146,6 @@ function on_exit --on-process %self end \endfish -Right after reading /usr/share/fish/config.fish and before reading /etc/fish/config.fish, fish will also read files ending in ".fish" in ~/.config/fish/conf.d/, /etc/fish/conf.d and /usr/share/fish/vendor_conf.d (the exact values depend on $XDG_CONFIG_HOME, $__fish_sysconfdir and $__fish_datadir). If there are files with the same name in two or all of these, fish will only attempt to read the first (skipping all files with that name if it is unreadable). ~/.config takes precedence over /etc/ which takes precedence over /usr. The path to the latter can also be gotten via `pkg-config` as "confdir", and is meant for third-party applications to integrate with fish. \section other Other features diff --git a/fish.pc.in b/fish.pc.in index c8fabd7e8..d110a2fbe 100644 --- a/fish.pc.in +++ b/fish.pc.in @@ -1,8 +1,8 @@ prefix=@prefix@ datadir=@datadir@ -completionsdir=${datadir}/fish/vendor_completions.d -functionsdir=${datadir}/fish/vendor_functions.d -confdir=${datadir}/fish/vendor_conf.d +completionsdir=@extra_completionsdir@ +functionsdir=@extra_functionsdir@ +confdir=@extra_snippetsdir@ Name: fish Description: fish, the friendly interactive shell diff --git a/fish.xcodeproj/project.pbxproj b/fish.xcodeproj/project.pbxproj index 0f7e5efc3..6d2ca9028 100644 --- a/fish.xcodeproj/project.pbxproj +++ b/fish.xcodeproj/project.pbxproj @@ -24,7 +24,6 @@ buildPhases = ( D07D266915E33B86009E43F6 /* CopyFiles */, D07D266B15E33B86009E43F6 /* Copy Files */, - D01A25E11AF58D8C002F9E92 /* ShellScript */, D01A2CA716965ADD00767098 /* CopyFiles */, ); dependencies = ( @@ -55,7 +54,6 @@ D0F019FC15A977B40034B3B1 /* CopyFiles */, D033780F15DC6D2A00A634BA /* CopyFiles */, D01A2C9B16964C8200767098 /* Copy Files */, - D01A25E01AF58CD0002F9E92 /* ShellScript */, ); dependencies = ( D0F01A1315AA36280034B3B1 /* PBXTargetDependency */, @@ -1048,32 +1046,6 @@ shellPath = /bin/sh; shellScript = ./build_tools/xcode_version_gen.sh; }; - D01A25E01AF58CD0002F9E92 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "mkdir -p -m 755 \"${TARGET_BUILD_DIR}/base/share/fish/vendor_completions.d\""; - }; - D01A25E11AF58D8C002F9E92 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 8; - files = ( - ); - inputPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 1; - shellPath = /bin/sh; - shellScript = "mkdir -p -m 755 \"${INSTALL_ROOT}/${INSTALL_PATH}/share/fish/vendor_completions.d\""; - }; D0A564EB168CFDDE00AF6161 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; diff --git a/share/__fish_build_paths.fish.in b/share/__fish_build_paths.fish.in new file mode 100644 index 000000000..4b923d7ef --- /dev/null +++ b/share/__fish_build_paths.fish.in @@ -0,0 +1,8 @@ +# @configure_input@ +# This function is generated by the autotools build to "compile in" +# the local "vendor" completions, functions and configuration snippets. +# It is sourced by share/config.fish, if it exists. + +set __extra_completionsdir @extra_completionsdir@ +set __extra_functionsdir @extra_functionsdir@ +set __extra_snippetsdir @extra_snippetsdir@ diff --git a/share/config.fish b/share/config.fish index fe45f70d7..3f7986525 100644 --- a/share/config.fish +++ b/share/config.fish @@ -45,11 +45,20 @@ end # __fish_datadir, __fish_sysconfdir, __fish_help_dir, __fish_bin_dir # are expected to have been set up by read_init from fish.cpp +# Grab extra directories (as specified by the build process, usually for +# third-party packages to ship completions &c. +set -l __extra_completionsdir +set -l __extra_functionsdir +set -l __extra_snippetsdir +if test -f $__fish_datadir/__fish_build_paths.fish + source $__fish_datadir/__fish_build_paths.fish +end + # Set up function and completion paths. Make sure that the fish # default functions/completions are included in the respective path. if not set -q fish_function_path - set fish_function_path $configdir/fish/functions $__fish_sysconfdir/functions $__fish_datadir/vendor_functions.d $__fish_datadir/functions + set fish_function_path $configdir/fish/functions $__fish_sysconfdir/functions $__extra_functionsdir $__fish_datadir/functions end if not contains $__fish_datadir/functions $fish_function_path @@ -57,7 +66,7 @@ if not contains $__fish_datadir/functions $fish_function_path end if not set -q fish_complete_path - set fish_complete_path $configdir/fish/completions $__fish_sysconfdir/completions $__fish_datadir/vendor_completions.d $__fish_datadir/completions $userdatadir/fish/generated_completions + set fish_complete_path $configdir/fish/completions $__fish_sysconfdir/completions $__extra_completionsdir $__fish_datadir/completions $userdatadir/fish/generated_completions end if not contains $__fish_datadir/completions $fish_complete_path @@ -155,9 +164,9 @@ function . --description 'Evaluate contents of file (deprecated, see "source")' end # As last part of initialization, source the conf directories -# Implement precedence (User > Admin > Vendors > Fish) by basically doing "basename" +# Implement precedence (User > Admin > Extra (e.g. vendors) > Fish) by basically doing "basename" set -l sourcelist -for file in $configdir/fish/conf.d/*.fish $__fish_sysconfdir/conf.d/*.fish $__fish_datadir/vendor_conf.d/*.fish +for file in $configdir/fish/conf.d/*.fish $__fish_sysconfdir/conf.d/*.fish $__extra_snippetsdir/*.fish set -l basename (string replace -r '^.*/' '' -- $file) contains -- $basename $sourcelist; and continue set sourcelist $sourcelist $basename From 8f33b55ccc501f8460d4d5ff8bd5ef7861a0ee3a Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Mon, 4 Apr 2016 16:55:40 -0700 Subject: [PATCH 058/363] remove unused special color "ignore" Resolve lint warning about unused method "rgb_color_t::ignore()". Fixes #2893 --- .gitignore | 2 ++ src/builtin_set_color.cpp | 6 +++--- src/color.cpp | 18 +++++------------- src/color.h | 15 ++------------- src/output.cpp | 22 +++++++--------------- src/output.h | 9 +++------ 6 files changed, 22 insertions(+), 50 deletions(-) diff --git a/.gitignore b/.gitignore index e0713b06e..717e32434 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,5 @@ lexicon.txt lexicon_filter lexicon.log DerivedData/ +compile_commands.json +xcodebuild.log diff --git a/src/builtin_set_color.cpp b/src/builtin_set_color.cpp index b3c7ebaff..449e38954 100644 --- a/src/builtin_set_color.cpp +++ b/src/builtin_set_color.cpp @@ -137,7 +137,7 @@ static int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t ** for (; w.woptind < argc; w.woptind++) { rgb_color_t fg = rgb_color_t(argv[w.woptind]); - if (fg.is_none() || fg.is_ignore()) + if (fg.is_none()) { streams.err.append_format(_(L"%ls: Unknown color '%ls'\n"), argv[0], argv[w.woptind]); return STATUS_BUILTIN_ERROR; @@ -155,10 +155,10 @@ static int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t ** // #1323: We may have multiple foreground colors. Choose the best one. // If we had no foreground coor, we'll get none(); if we have at least one we expect not-none const rgb_color_t fg = best_color(fgcolors, output_get_color_support()); - assert(fgcolors.empty() || !(fg.is_none() || fg.is_ignore())); + assert(fgcolors.empty() || !fg.is_none()); const rgb_color_t bg = rgb_color_t(bgcolor ? bgcolor : L""); - if (bgcolor && (bg.is_none() || bg.is_ignore())) + if (bgcolor && bg.is_none()) { streams.err.append_format(_(L"%ls: Unknown color '%ls'\n"), argv[0], bgcolor); return STATUS_BUILTIN_ERROR; diff --git a/src/color.cpp b/src/color.cpp index 19adc390f..4c3c89d21 100644 --- a/src/color.cpp +++ b/src/color.cpp @@ -1,6 +1,4 @@ -/** \file color.cpp Color class implementation -*/ - +// Color class implementation. #include "color.h" #include "fallback.h" // IWYU pragma: keep #include @@ -21,10 +19,6 @@ bool rgb_color_t::try_parse_special(const wcstring &special) { this->type = type_reset; } - else if (! wcscasecmp(name, L"ignore")) - { - this->type = type_ignore; - } else { this->type = type_none; @@ -240,22 +234,22 @@ 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); @@ -382,8 +376,6 @@ wcstring rgb_color_t::description() const return L"reset"; case type_normal: return L"normal"; - case type_ignore: - return L"ignore"; default: abort(); return L""; diff --git a/src/color.h b/src/color.h index 988bfdb8a..1faa35a6b 100644 --- a/src/color.h +++ b/src/color.h @@ -1,5 +1,4 @@ -/** \file color.h Color class. - */ +// Color class. #ifndef FISH_COLOR_H #define FISH_COLOR_H @@ -24,8 +23,7 @@ class rgb_color_t type_named, type_rgb, type_normal, - type_reset, - type_ignore + type_reset }; unsigned char type:4; @@ -79,18 +77,9 @@ class rgb_color_t /** Returns the normal special color */ static rgb_color_t normal(); - /** Returns the ignore special color */ - static rgb_color_t ignore(); - /** Returns the none special color */ static rgb_color_t none(); - /** Returns whether the color is the ignore special color */ - bool is_ignore(void) const - { - return type == type_ignore; - } - /** Returns whether the color is the normal special color */ bool is_normal(void) const { diff --git a/src/output.cpp b/src/output.cpp index dbd97eb4b..033d14098 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -246,25 +246,17 @@ void set_color(rgb_color_t c, rgb_color_t c2) was_underline=0; } - if (! last_color2.is_normal() && - ! last_color2.is_reset() && - ! last_color2.is_ignore()) + if (!last_color2.is_normal() && !last_color2.is_reset()) { - /* - Background was set - */ - last_bg_set=1; + // Background was set. + last_bg_set = 1; } - if (! c2.is_normal() && - ! c2.is_ignore()) + if (!c2.is_normal()) { - /* - Background is set - */ - bg_set=1; - if (c==c2) - c = (c2==rgb_color_t::white())?rgb_color_t::black():rgb_color_t::white(); + // 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)) diff --git a/src/output.h b/src/output.h index 68ad2a2e5..acdd26127 100644 --- a/src/output.h +++ b/src/output.h @@ -27,9 +27,7 @@ enum FISH_COLOR_MAGENTA, FISH_COLOR_CYAN, FISH_COLOR_WHITE, - /** The default fg color of the terminal */ - FISH_COLOR_NORMAL, - FISH_COLOR_IGNORE, + FISH_COLOR_NORMAL, // the default fg color of the terminal FISH_COLOR_RESET }; @@ -41,9 +39,8 @@ enum screen to flicker, the function takes care to write as little as possible. - Possible values for color are any form the FISH_COLOR_* enum, - FISH_COLOR_IGNORE and FISH_COLOR_RESET. FISH_COLOR_IGNORE will - leave the color unchanged, and FISH_COLOR_RESET will perform an + Possible values for color are any form the FISH_COLOR_* enum + and FISH_COLOR_RESET. FISH_COLOR_RESET will perform an exit_attribute_mode, even if set_color thinks it is already in FISH_COLOR_NORMAL mode. From a4642f141f7781792909cff385ff2316530416ee Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Sat, 2 Apr 2016 22:19:44 -0700 Subject: [PATCH 059/363] don't try to use st_gen (inode generation) number Per discussion in pull-request #2891, it's not available on Linux (we just fill it with zero), and unless run as root on OS X (or other BSD system) it will be zero. Remove it from file_id_t. Also fix the initialization of the file_id_t structure. Fixes #2891 --- src/wutil.cpp | 10 ++-------- src/wutil.h | 3 +-- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/wutil.cpp b/src/wutil.cpp index a4494470d..0b11989a4 100644 --- a/src/wutil.cpp +++ b/src/wutil.cpp @@ -28,7 +28,7 @@ typedef std::string cstring; -const file_id_t kInvalidFileID = {(dev_t)-1LL, (ino_t)-1LL, (uint64_t)-1LL, -1, -1, (uint32_t)-1}; +const file_id_t kInvalidFileID = {(dev_t)-1LL, (ino_t)-1LL, (uint64_t)-1LL, -1, -1, -1, -1}; #ifndef PATH_MAX #ifdef MAXPATHLEN @@ -555,12 +555,7 @@ file_id_t file_id_t::file_id_from_stat(const struct stat *buf) result.change_nanoseconds = 0; result.mod_nanoseconds = 0; #endif - -#if defined(__APPLE__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) - result.generation = buf->st_gen; -#else - result.generation = 0; -#endif + return result; } @@ -619,7 +614,6 @@ int file_id_t::compare_file_id(const file_id_t &rhs) const if (! ret) ret = compare(device, rhs.device); if (! ret) ret = compare(inode, rhs.inode); if (! ret) ret = compare(size, rhs.size); - if (! ret) ret = compare(generation, rhs.generation); if (! ret) ret = compare(change_seconds, rhs.change_seconds); if (! ret) ret = compare(change_nanoseconds, rhs.change_nanoseconds); if (! ret) ret = compare(mod_seconds, rhs.mod_seconds); diff --git a/src/wutil.h b/src/wutil.h index a14dfa9b1..f16e28c06 100644 --- a/src/wutil.h +++ b/src/wutil.h @@ -143,8 +143,7 @@ struct file_id_t long change_nanoseconds; time_t mod_seconds; long mod_nanoseconds; - uint32_t generation; - + bool operator==(const file_id_t &rhs) const; bool operator!=(const file_id_t &rhs) const; From fd1b7ba52901179c19f5997b650518993d919f52 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Fri, 1 Apr 2016 20:48:11 -0700 Subject: [PATCH 060/363] support making fish code match the style guide This changes implements two new make targets: `style` and `style-all`. These make it easy to ensure that a change conforms to the project style guides for C++ and fish code. Fixes #571 --- .clang-format | 8 +++ CONTRIBUTING.md | 135 ++++++++++++++++++----------------------- Makefile.in | 10 ++- build_tools/lint.fish | 21 +++---- build_tools/style.fish | 90 +++++++++++++++++++++++++++ 5 files changed, 174 insertions(+), 90 deletions(-) create mode 100644 .clang-format mode change 100755 => 100644 build_tools/lint.fish create mode 100755 build_tools/style.fish diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..d154da600 --- /dev/null +++ b/.clang-format @@ -0,0 +1,8 @@ +# Use the Google style with these modifications: +# +# 1) lines can be up to 100 chars long rather than 80, and +# 2) use a four space indent rather than two spaces. +# +BasedOnStyle: Google +ColumnLimit: 100 +IndentWidth: 4 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6321cdc7f..af2ad6907 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,13 +1,57 @@ # Guidelines For Developers +This document provides guidelines for making changes to the fish-shell project. This includes rules for how to format the code, naming conventions, etc. It also includes recommended best practices such as creating a Travis-CI account so you can verify your changes pass all the tests before making a pull-request. + +See the bottom of this document for help on installing the linting and style reformatting tools discussed in the following sections. + ## Lint Free Code -Automated analysis tools like cppcheck and oclint can help identify bugs. They also help ensure the code has a consistent style and avoids patterns that tend to confuse people. +Automated analysis tools like cppcheck and oclint can point out potential bugs. They also help ensure the code has a consistent style and that it avoids patterns that tend to confuse people. Ultimately we want lint free code. However, at the moment a lot of cleanup is required to reach that goal. For now simply try to avoid introducing new lint. To make linting the code easy there are two make targets: `lint` and `lint-all`. The latter does just what the name implies. The former will lint any modified but not committed `*.cpp` files. If there is no uncommitted work it will lint the files in the most recent commit. +## Ensuring Your Changes Conform to the Style Guides + +The following sections discuss the specific rules for the style that should be used when writing fish code. To ensure your changes conform to the style rules you simply need to run + +``` +make style +``` + +before commiting your change. If you've already committed your changes that's okay since it will then check the files in the most recent commit. This can be useful after you've merged someone elses change and want to check that it's style is acceptable. + +If you want to check the style of the entire code base run + +``` +make style-all +``` + +## Fish Script Style Guide + +Fish scripts such as those in the *share/functions* and *tests* directories should be formatted using the `fish_indent` command. + +Function names should be all lowercase with undescores separating words. Private functions should begin with an underscore. The first word should be `fish` if the function is unique to fish. + +The first word of global variable names should generally be `fish` for public vars or `_fish` for private vars to minimize the possibility of name clashes with user defined vars. + +## C++ Style Guide + +1. The [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html) forms the basis of the fish C++ style guide. There are two major deviations for the fish project. First, a four, rather than two, space indent. Second, line lengths up to 100, rather than 80, characters. + +1. The `clang-format` command is authoritative with respect to indentation, whitespace around operators, etc. **Note**: this rule should be ignored at this time. After the code is cleaned up this rule will become mandatory. + +1. All names in code should be `small_snake_case`. No Hungarian notation is used. Classes and structs names should be followed by `_t`. + +1. Always attach braces to the surrounding context. + +1. Indent with spaces, not tabs and use four spaces per indent. + +## Installing the Required Tools + +### Installing the Linting Tools + To install the lint checkers on Mac OS X using HomeBrew: ``` @@ -24,85 +68,24 @@ sudo apt-get install oclint sudo apt-get install cppcheck ``` -## Fish Script Style Guide +### Installing the Reformatting Tools -Fish scripts such as those in the *share/functions* and *tests* directories should be formatted using the `fish_indent` command. +To install the reformatting tool on Mac OS X using HomeBrew: -Function names should be all lowercase with undescores separating words. Private functions should begin with an underscore. The first word should be `fish` if the function is unique to fish. +``` +brew install clang-format +``` -The first word of global variable names should generally be `fish` for public vars or `_fish` for private vars to minimize the possibility of name clashes with user defined vars. +To install the reformatting tool on Linux distros that use Apt: -## C++ Style Guide +``` +apt-cache search clang-format +``` -1. The `clang-format` command is authoritative with respect to indentation, whitespace around operators, etc. **Note**: this rule should be ignored at this time. A subsequent commit will add the necessary config file and make targets. After the happens the code will be cleaned up and this rule will become mandatory. +That will list the versions available. Pick the newest one available (3.6 for Ubuntu 14.04 as I write this) and install it: -1. All names in code should be `small_snake_case`. No Hungarian notation is used. Classes and structs names should be followed by `_t`. +``` +sudo apt-get install clang-format-3.6 +sudo ln -s /usr/bin/clang-format-3.6 /usr/bin/clang-format -1. fish uses the Allman/BSD style of indentation. - -1. Indent with spaces, not tabs. - -1. Use 4 spaces per indent. - -1. Opening curly bracket is on the following line: - - // ✔: - struct name - { - // code - }; - - void func() - { - // code - } - - if (...) - { - // code - } - - // ✗: - void func() { - // code - } - -1. Put space after `if`, `while` and `for` before conditions. - - // ✔: - if () {} - - // ✗: - if() {} - -1. Put spaces before and after operators excluding increment and decrement; - - // ✔: - int a = 1 + 2 * 3; - a++; - - // ✗: - int a=1+2*3; - a ++; - -1. Never put spaces between function name and parameters list. - - // ✔: - func(args); - - // ✗: - func (args); - -1. Never put spaces after `(` and before `)`. - -1. Always put space after comma and semicolon. - - // ✔: - func(arg1, arg2); - - for (int i = 0; i < LENGTH; i++) {} - - // ✗: - func(arg1,arg2); - - for (int i = 0;i&1 + cppcheck -q --verbose --std=posix --std=c11 --language=c++ --template "[{file}:{line}]: {severity} ({id}): {message}" --suppress=missingIncludeSystem --inline-suppr --enable=$cppchecks $cppcheck_args $c_files 2>& 1 end if type -q oclint @@ -82,20 +78,19 @@ if set -q c_files[1] oclint-xcodebuild xcodebuild.log > /dev/null end if test $all = yes - oclint-json-compilation-database -e '/pcre2-10.20/' \ - -- -enable-global-analysis 2>&1 + oclint-json-compilation-database -e '/pcre2-10.20/' -- -enable-global-analysis 2>& 1 else set i_files for f in $c_files set i_files $i_files -i $f end echo oclint-json-compilation-database -e '/pcre2-10.20/' $i_files - oclint-json-compilation-database -e '/pcre2-10.20/' $i_files 2>&1 + oclint-json-compilation-database -e '/pcre2-10.20/' $i_files 2>& 1 end else # Presumably we're on Linux or other platform not requiring special # handling for oclint to work. - oclint $c_files -- $argv 2>&1 + oclint $c_files -- $argv 2>& 1 end end else diff --git a/build_tools/style.fish b/build_tools/style.fish new file mode 100755 index 000000000..76cf920d5 --- /dev/null +++ b/build_tools/style.fish @@ -0,0 +1,90 @@ +#!/usr/bin/env fish +# +# This is meant to be run by "make style" or "make style-all". It is not meant to +# be run directly from a shell prompt although it can be. +# +# This runs C++ files and fish scripts (*.fish) through their respective code +# formatting programs. +# +set c_files +set f_files +set all no + +if test "$argv[1]" = "--all" + set all yes + set -e argv[1] +end + +if set -q argv[1] + echo "Unexpected arguments: '$argv'" + exit 1 +end + +if test $all = yes + set c_files src/*.h src/*.cpp + set f_files ***.fish +else + # We haven't been asked to reformat all the source. If there are uncommitted + # changes reformat those, else reformat the files in the most recent commit. + set pending (git status --porcelain --short --untracked-files=all | sed -e 's/^ *//') + if count $pending > /dev/null + # There are pending changes so lint those files. + for arg in $pending + set files $files (string split -m 1 ' ' $arg)[2] + end + else + # No pending changes so lint the files in the most recent commit. + set files (git show --name-only --pretty=oneline head | tail --lines=+2) + end + + # Extract just the C/C++ files. + set c_files (string match -r '^.*\.(?:c|cpp|h)$' -- $files) + # Extract just the fish files. + set f_files (string match -r '^.*\.fish$' -- $files) +end + +# Run the C++ reformatter if we have any C++ files. +if set -q c_files[1] + if type -q clang-format + echo + echo ======================================== + echo Running clang-format + echo ======================================== + for file in $c_files + clang-format $file > $file.new + if cmp --quiet $file $file.new + echo $file was correctly formatted + rm $file.new + else + echo $file was NOT correctly formatted + mv $file.new $file + end + end + else + echo + echo 'WARNING: Cannot find clang-format command' + echo + end +end + +# Run the fish reformatter if we have any fish files. +if set -q f_files[1] + if not type -q fish_indent + make fish_indent + set PATH . $PATH + end + echo + echo ======================================== + echo Running fish_indent + echo ======================================== + for file in $f_files + fish_indent < $file > $file.new + if cmp --quiet $file $file.new + echo $file was correctly formatted + rm $file.new + else + echo $file was NOT correctly formatted + mv $file.new $file + end + end +end From 3435e94994268caba16e67780bb71381332334b7 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Mon, 4 Apr 2016 21:32:03 -0700 Subject: [PATCH 061/363] make the string man page more readable I didn't notice when I merged commit cb6d5d76c8232264f66c9a1db895d9dd69d77bc3 by thebespokepixel.com that it removed the explicit wrapping in the `string` man page. That makes `man string` harder to read so reinstate the explicit wrapping. --- doc_src/string.txt | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/doc_src/string.txt b/doc_src/string.txt index fd77da945..1a3f2ea11 100644 --- a/doc_src/string.txt +++ b/doc_src/string.txt @@ -3,13 +3,18 @@ \subsection string-synopsis Synopsis \fish{synopsis} string length [(-q | --quiet)] [STRING...] -string sub [(-s | --start) START] [(-l | --length) LENGTH] [(-q | --quiet)] [STRING...] -string split [(-m | --max) MAX] [(-r | --right)] [(-q | --quiet)] SEP [STRING...] +string sub [(-s | --start) START] [(-l | --length) LENGTH] [(-q | --quiet)] + [STRING...] +string split [(-m | --max) MAX] [(-r | --right)] [(-q | --quiet)] SEP + [STRING...] string join [(-q | --quiet)] SEP [STRING...] -string trim [(-l | --left)] [(-r | --right)] [(-c | --chars CHARS)] [(-q | --quiet)] [STRING...] +string trim [(-l | --left)] [(-r | --right)] [(-c | --chars CHARS)] + [(-q | --quiet)] [STRING...] string escape [(-n | --no-quoted)] [STRING...] -string match [(-a | --all)] [(-i | --ignore-case)] [(-r | --regex)] [(-n | --index)] [(-q | --quiet)] PATTERN [STRING...] -string replace [(-a | --all)] [(-i | --ignore-case)] [(-r | --regex)] [(-q | --quiet)] PATTERN REPLACEMENT [STRING...] +string match [(-a | --all)] [(-i | --ignore-case)] [(-r | --regex)] + [(-n | --index)] [(-q | --quiet)] PATTERN [STRING...] +string replace [(-a | --all)] [(-i | --ignore-case)] [(-r | --regex)] + [(-q | --quiet)] PATTERN REPLACEMENT [STRING...] \endfish From 9e93ddc0978644d0670e192052619bb4710041e7 Mon Sep 17 00:00:00 2001 From: Mark Griffiths Date: Tue, 5 Apr 2016 13:31:31 +0100 Subject: [PATCH 062/363] Fix a couple of minor issues in string examples Print correct return code in 2nd example Remove syntax colouring in \cg Signed-off-by: Mark Griffiths --- doc_src/string.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc_src/string.txt b/doc_src/string.txt index 1a3f2ea11..bae21dc99 100644 --- a/doc_src/string.txt +++ b/doc_src/string.txt @@ -57,7 +57,7 @@ The following subcommands are available: >_ set str foo >_ string length -q $str; echo $status -1 +0 # Equivalent to test -n $str \endfish @@ -103,7 +103,7 @@ The following subcommands are available: \fish{cli-dark} >_ echo \\x07 | string escape -cg +cg \endfish \subsection string-example-match-glob Match Glob Examples From 200a10e78d0df7001c6c2a7c71e7139cccf4277c Mon Sep 17 00:00:00 2001 From: David Adam Date: Tue, 5 Apr 2016 22:22:12 +0800 Subject: [PATCH 063/363] Rename "snippets" to "conf" internally, and document them as snippets Discussed in #2896. --- Makefile.in | 6 +++--- configure.ac | 12 ++++++------ doc_src/index.hdr.in | 2 +- fish.pc.in | 2 +- share/__fish_build_paths.fish.in | 4 ++-- share/config.fish | 4 ++-- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Makefile.in b/Makefile.in index b176c5f69..ea008211a 100644 --- a/Makefile.in +++ b/Makefile.in @@ -55,7 +55,7 @@ docdir = @docdir@ localedir = @localedir@ extra_completionsdir = @extra_completionsdir@ extra_functionsdir = @extra_functionsdir@ -extra_snippetsdir = @extra_snippetsdir@ +extra_confdir = @extra_confdir@ # # pcre2 @@ -472,7 +472,7 @@ doc.h: $(HDR_FILES) -e "s,@docdir\@,$(docdir),g" \ -e "s,@extra_completionsdir\@,$(extra_completionsdir),g" \ -e "s,@extra_functionsdir\@,$(extra_functionsdir),g" \ - -e "s,@extra_snippetsdir\@,$(extra_snippetsdir),g" \ + -e "s,@extra_confdir\@,$(extra_confdir),g" \ -e "s|@configure_input\@|$@, generated from $@.in by the Makefile. DO NOT MANUALLY EDIT THIS FILE!|g" \ -e "s,@prefix\@,$(prefix),g" \ -e "s,@fish_build_version\@,$(FISH_BUILD_VERSION),g" \ @@ -665,7 +665,7 @@ install-force: all install-translations $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/completions $(INSTALL) -m 755 -d $(DESTDIR)$(extra_completionsdir) $(INSTALL) -m 755 -d $(DESTDIR)$(extra_functionsdir) - $(INSTALL) -m 755 -d $(DESTDIR)$(extra_snippetsdir) + $(INSTALL) -m 755 -d $(DESTDIR)$(extra_confdir) $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/functions $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/man/man1 $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/tools diff --git a/configure.ac b/configure.ac index f31825ce8..72a210fe6 100644 --- a/configure.ac +++ b/configure.ac @@ -877,12 +877,12 @@ AC_ARG_WITH([extra_functionsdir], [extra_functionsdir=$withval], [extra_functionsdir='${datadir}/fish/vendor_functions.d']) -AC_SUBST(extra_snippetsdir) -AC_ARG_WITH([extra-snippetsdir], - AS_HELP_STRING([--with-extra-snippetsdir=DIR], - [path for extra snippets]), - [extra_snippetsdir=$withval], - [extra_snippetsdir='${datadir}/fish/vendor_conf.d']) +AC_SUBST(extra_confdir) +AC_ARG_WITH([extra-confdir], + AS_HELP_STRING([--with-extra-confdir=DIR], + [path for extra conf]), + [extra_confdir=$withval], + [extra_confdir='${datadir}/fish/vendor_conf.d']) # Tell the world what we know. AC_CONFIG_FILES([Makefile]) diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index b75ee5e1d..7e889d8e6 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -1111,7 +1111,7 @@ On startup, Fish evaluates a number of configuration files, which can be used to Configuration files are evaluated in the following order: - Configuration shipped with fish, which should not be edited, usually `/usr/share/fish/config.fish`. - System-wide configuration files, where administrators can include initialization that should be run for all users on the system - similar to `/etc/profile` for POSIX-style shells - usually `/etc/fish/config.fish`; -- "Unit" configuration files ending in `.fish`, in the directories: +- Configuration snippets in files ending in `.fish`, in the directories: - `~/.config/fish/conf.d/` - `/etc/fish/conf.d` - `/usr/share/fish/vendor_conf.d` diff --git a/fish.pc.in b/fish.pc.in index d110a2fbe..04fd75ce8 100644 --- a/fish.pc.in +++ b/fish.pc.in @@ -2,7 +2,7 @@ prefix=@prefix@ datadir=@datadir@ completionsdir=@extra_completionsdir@ functionsdir=@extra_functionsdir@ -confdir=@extra_snippetsdir@ +confdir=@extra_confdir@ Name: fish Description: fish, the friendly interactive shell diff --git a/share/__fish_build_paths.fish.in b/share/__fish_build_paths.fish.in index 4b923d7ef..8308cf50c 100644 --- a/share/__fish_build_paths.fish.in +++ b/share/__fish_build_paths.fish.in @@ -1,8 +1,8 @@ # @configure_input@ # This function is generated by the autotools build to "compile in" -# the local "vendor" completions, functions and configuration snippets. +# the local "vendor" completions, functions and configuration conf. # It is sourced by share/config.fish, if it exists. set __extra_completionsdir @extra_completionsdir@ set __extra_functionsdir @extra_functionsdir@ -set __extra_snippetsdir @extra_snippetsdir@ +set __extra_confdir @extra_confdir@ diff --git a/share/config.fish b/share/config.fish index 3f7986525..86e8d9b7d 100644 --- a/share/config.fish +++ b/share/config.fish @@ -49,7 +49,7 @@ end # third-party packages to ship completions &c. set -l __extra_completionsdir set -l __extra_functionsdir -set -l __extra_snippetsdir +set -l __extra_confdir if test -f $__fish_datadir/__fish_build_paths.fish source $__fish_datadir/__fish_build_paths.fish end @@ -166,7 +166,7 @@ end # As last part of initialization, source the conf directories # Implement precedence (User > Admin > Extra (e.g. vendors) > Fish) by basically doing "basename" set -l sourcelist -for file in $configdir/fish/conf.d/*.fish $__fish_sysconfdir/conf.d/*.fish $__extra_snippetsdir/*.fish +for file in $configdir/fish/conf.d/*.fish $__fish_sysconfdir/conf.d/*.fish $__extra_confdir/*.fish set -l basename (string replace -r '^.*/' '' -- $file) contains -- $basename $sourcelist; and continue set sourcelist $sourcelist $basename From 8e8b5a648197ca83af50064bc9492ca855e9d705 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Tue, 5 Apr 2016 18:49:06 -0700 Subject: [PATCH 064/363] augment the guide for contributing Include information about how to deal with lint warnings and suppress `clang-format` reformatting of blocks of code. Move information only relevant to developers from the README.md to the CONTRIBUTING.md document. Closes #2901 --- CONTRIBUTING.md | 62 +++++++++++++++++++++++++++++++++++++++++++ README.md | 30 +++------------------ build_tools/lint.fish | 0 3 files changed, 66 insertions(+), 26 deletions(-) mode change 100644 => 100755 build_tools/lint.fish diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index af2ad6907..2c8473190 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,6 +12,26 @@ Ultimately we want lint free code. However, at the moment a lot of cleanup is re To make linting the code easy there are two make targets: `lint` and `lint-all`. The latter does just what the name implies. The former will lint any modified but not committed `*.cpp` files. If there is no uncommitted work it will lint the files in the most recent commit. +### Dealing With Lint Warnings + +You are strongly encouraged to address a lint warning by refactoring the code, changing variable names, or whatever action is implied by the warning. + +### Suppressing Lint Warnings + +Once in a while the lint tools emit a false positive warning. For example, cppcheck might suggest a memory leak is present when that is not the case. To suppress that cppcheck warning you should insert a line like the following immediately prior to the line cppcheck warned about: + +``` +// cppcheck-suppress memleak // addr not really leaked +``` + +The explanatory portion of the suppression comment is optional. For other types of warnings replace "memleak" with the value inside the parenthesis (e.g., "nullPointerRedundantCheck") from a warning like the following: + +``` +[src/complete.cpp:1727]: warning (nullPointerRedundantCheck): Either the condition 'cmd_node' is redundant or there is possible null pointer dereference: cmd_node. +``` + +Suppressing oclint warnings is more complicated to describe so I'll refer you to the [OCLint HowTo](http://docs.oclint.org/en/latest/howto/suppress.html#annotations) on the topic. + ## Ensuring Your Changes Conform to the Style Guides The following sections discuss the specific rules for the style that should be used when writing fish code. To ensure your changes conform to the style rules you simply need to run @@ -28,6 +48,16 @@ If you want to check the style of the entire code base run make style-all ``` +### Suppressing Reformatting of the Code + +If you have a good reason for doing so you can tell `clang-format` to not reformat a block of code by enclosing it in comments like this: + +``` +// clang-format off +code to ignore +// clang-format on +``` + ## Fish Script Style Guide Fish scripts such as those in the *share/functions* and *tests* directories should be formatted using the `fish_indent` command. @@ -48,6 +78,38 @@ The first word of global variable names should generally be `fish` for public va 1. Indent with spaces, not tabs and use four spaces per indent. +1. Comments should always use the C++ style; i.e., each line of the comment should begin with a `//` and should be limited to 100 characters. Comments that do not begin a line should be separated from the previous text by two spaces. + +## Testing + +The source code for fish includes a large collection of tests. If you are making any changes to fish, running these tests is highly recommended to make sure the behaviour remains consistent. + +You are also strongly encouraged to add tests when changing the functionality of fish. Especially if you are fixing a bug to help ensure there are no regressions in the future (i.e., we don't reintroduce the bug). + +### Local testing + +The tests can be run on your local computer on all operating systems. + +Running the tests is only supported from the autotools build and not xcodebuild. On OS X, you will need to install autoconf — we suggest using [Homebrew](http://brew.sh/) to install these tools. + + autoconf + ./configure + make test [gmake on BSD] + +### Travis CI Build and Test + +The Travis Continuous Integration services can be used to test your changes using multiple configurations. This is the same service that the fish shell project uses to ensure new changes haven't broken anything. Thus it is a really good idea that you leverage Travis CI before making a pull-request to avoid embarrasment at breaking the build. + +You will need to [fork the fish-shell repository on GitHub](https://help.github.com/articles/fork-a-repo/). Then setup Travis to test your changes before you make a pull-request: + +1. [Sign in to Travis CI](https://travis-ci.org/auth) with your GitHub account, accepting the GitHub access permissions confirmation. +1. Once you're signed in, and your repositories are synchronised, go to your [profile page](https://travis-ci.org/profile) and enable the fish-shell repository. +1. Push your changes to GitHub. + +You'll receive an email when the tests are complete telling you whether or not any tests failed. + +You'll find the configuration used to control Travis in the `.travis.yml` file. + ## Installing the Required Tools ### Installing the Linting Tools diff --git a/README.md b/README.md index fc549ef61..648e8eb63 100644 --- a/README.md +++ b/README.md @@ -54,32 +54,6 @@ On RedHat, CentOS, or Amazon EC2: sudo yum install ncurses-devel -## Testing - -The source code for fish includes a large collection of tests. If you are making any changes to fish, running these tests is highly recommended to make sure the behaviour remains consistent. - -### Local testing - -The tests can be run on your local computer on all operating systems. - -Running the tests is only supported from the Autotools build. On OS X, you will require an installation of autoconf; we suggest using [Homebrew](http://brew.sh/) to install these tools. - - autoconf - ./configure - make test [gmake on BSD] - -### Travis CI Build and Test - -The Travis Continuous Integration services can be used to test your changes across multiple platforms. You will need to [fork the fish-shell repository on GitHub](https://help.github.com/articles/fork-a-repo/), or push a copy of the code to your GitHub account. - - 1. [Sign in to Travis CI](https://travis-ci.org/auth) with your GitHub account, accepting the GitHub access permissions confirmation. - 2. Once you're signed in, and your repositories are synchronised, go to your [profile page](https://travis-ci.org/profile) and enable the fish-shell repository. - 3. Push your changes to GitHub. - -You'll receive an email when the tests are complete telling you whether or not any tests failed. - -You'll find the configuration used to control Travis in the `.travis.yml` file. - ## Runtime Dependencies fish requires a curses implementation, such as ncurses, to run. @@ -116,6 +90,10 @@ To switch your default shell back, you can run: Substitute /bin/bash with /bin/tcsh or /bin/zsh as appropriate. +## Contributing Changes to the Code + +See the [Guide for Developers](CONTRIBUTING.md). + ## Contact Us Questions, comments, rants and raves can be posted to the official fish mailing list at or join us on our [gitter.im channel](https://gitter.im/fish-shell/fish-shell) or IRC channel [#fish at irc.oftc.net](https://webchat.oftc.net/?channels=fish). Or use the [fish tag on Stackoverflow](https://stackoverflow.com/questions/tagged/fish). diff --git a/build_tools/lint.fish b/build_tools/lint.fish old mode 100644 new mode 100755 From 4ff8e6e7818747d805ac44be0a36aedef4deaa5c Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Tue, 5 Apr 2016 15:43:24 -0700 Subject: [PATCH 065/363] change how redirections are formatted Modify `fish_indent` to emit redirections without a space before the target of the redirection; e.g., "2>&1" rather than "2>& 1" as the former is clearer to humans. Fixes #2899 --- doc_src/fish_indent.txt | 2 + src/fish_indent.cpp | 103 +++++++++++++++++++++++++++++----------- src/parse_constants.h | 6 ++- src/parse_tree.cpp | 43 +++++++++++++++++ tests/indent.in | 6 +++ tests/indent.out | 4 ++ 6 files changed, 135 insertions(+), 29 deletions(-) diff --git a/doc_src/fish_indent.txt b/doc_src/fish_indent.txt index 95b3c1ec8..b4cf13c0d 100644 --- a/doc_src/fish_indent.txt +++ b/doc_src/fish_indent.txt @@ -11,6 +11,8 @@ fish_indent [OPTIONS] The following options are available: +- `-d` or `--dump` dumps information about the parsed fish commands to stderr + - `-i` or `--no-indent` do not indent commands; only reformat to one job per line - `-v` or `--version` displays the current fish version and then exits diff --git a/src/fish_indent.cpp b/src/fish_indent.cpp index 037d9b773..0e31f28e7 100644 --- a/src/fish_indent.cpp +++ b/src/fish_indent.cpp @@ -15,10 +15,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -/** \file fish_indent.cpp - The fish_indent proegram. -*/ - +// The fish_indent proegram. #include "config.h" #include @@ -45,10 +42,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA #define SPACES_PER_INDENT 4 -/* An indent_t represents an abstract indent depth. 2 means we are in a doubly-nested block, etc. */ +// An indent_t represents an abstract indent depth. 2 means we are in a doubly-nested block, etc. typedef unsigned int indent_t; +static bool dump_parse_tree = false; -/* Read the entire contents of a file into the specified string */ +// Read the entire contents of a file into the specified string. static wcstring read_file(FILE *f) { wcstring result; @@ -69,10 +67,12 @@ static wcstring read_file(FILE *f) return result; } -/* Append whitespace as necessary. If we have a newline, append the appropriate indent. Otherwise, append a space. */ -static void append_whitespace(indent_t node_indent, bool do_indent, bool has_new_line, wcstring *out_result) +// Append whitespace as necessary. If we have a newline, append the appropriate indent. Otherwise, +// append a space. +static void append_whitespace(indent_t node_indent, bool do_indent, bool has_new_line, + wcstring *out_result) { - if (! has_new_line) + if (!has_new_line) { out_result->push_back(L' '); } @@ -82,26 +82,62 @@ static void append_whitespace(indent_t node_indent, bool do_indent, bool has_new } } -static void prettify_node_recursive(const wcstring &source, const parse_node_tree_t &tree, node_offset_t node_idx, indent_t node_indent, parse_token_type_t parent_type, bool *has_new_line, wcstring *out_result, bool do_indent) +// Dump a parse tree node in a form helpful to someone debugging the behavior of this program. +static void dump_node(indent_t node_indent, const parse_node_t &node, const wcstring &source) +{ + int nextc_idx = node.source_start + node.source_length; + wchar_t prevc = node.source_start > 0 ? source[node.source_start - 1] : L' '; + wchar_t nextc = nextc_idx < source.size() ? source[nextc_idx] : L' '; + wchar_t prevc_str[4] = {prevc, 0, 0, 0}; + wchar_t nextc_str[4] = {nextc, 0, 0, 0}; + if (prevc < L' ') + { + prevc_str[0] = L'\\'; + prevc_str[1] = L'c'; + prevc_str[2] = prevc + '@'; + } + if (nextc < L' ') + { + nextc_str[0] = L'\\'; + nextc_str[1] = L'c'; + nextc_str[2] = nextc + '@'; + } + fwprintf(stderr, L"{off %4d, len %4d, indent %2u, %ls} [%ls|%ls|%ls]\n", + node.source_start, node.source_length, node_indent, + parser_token_types[node.type].c_str(), prevc_str, source.substr(node.source_start, + node.source_length).c_str(), nextc_str); +} + +static void prettify_node_recursive(const wcstring &source, const parse_node_tree_t &tree, + node_offset_t node_idx, indent_t node_indent, parse_token_type_t parent_type, + bool *has_new_line, wcstring *out_result, bool do_indent) { const parse_node_t &node = tree.at(node_idx); const parse_token_type_t node_type = node.type; + const parse_token_type_t prev_node_type = node_idx > 0 ? + tree.at(node_idx - 1).type : token_type_invalid; - /* Increment the indent if we are either a root job_list, or root case_item_list, or in an if or while header (#1665) */ - const bool is_root_job_list = (node_type == symbol_job_list && parent_type != symbol_job_list); - const bool is_root_case_item_list = (node_type == symbol_case_item_list && parent_type != symbol_case_item_list); - const bool is_if_while_header = ((node_type == symbol_job || node_type == symbol_andor_job_list) && - (parent_type == symbol_if_clause || parent_type == symbol_while_header)); - if (is_root_job_list || is_root_case_item_list || is_if_while_header) + // Increment the indent if we are either a root job_list, or root case_item_list, or in an if or + // while header (#1665). + const bool is_root_job_list = node_type == symbol_job_list && parent_type != symbol_job_list; + const bool is_root_case_list = node_type == symbol_case_item_list && + parent_type != symbol_case_item_list; + const bool is_if_while_header = \ + (node_type == symbol_job || node_type == symbol_andor_job_list) && + (parent_type == symbol_if_clause || parent_type == symbol_while_header); + + if (is_root_job_list || is_root_case_list || is_if_while_header) { node_indent += 1; } - /* Handle comments, which come before the text */ - if (node.has_comments()) + if (dump_parse_tree) dump_node(node_indent, node, source); + + if (node.has_comments()) // handle comments, which come before the text { - const parse_node_tree_t::parse_node_list_t comment_nodes = tree.comment_nodes_for_node(node); - for (size_t i=0; i < comment_nodes.size(); i++) + const parse_node_tree_t::parse_node_list_t comment_nodes = ( + tree.comment_nodes_for_node(node)); + for (size_t i = 0; i < comment_nodes.size(); i++) { const parse_node_t &comment_node = *comment_nodes.at(i); append_whitespace(node_indent, do_indent, *has_new_line, out_result); @@ -111,26 +147,30 @@ static void prettify_node_recursive(const wcstring &source, const parse_node_tre if (node_type == parse_token_type_end) { - /* Newline */ out_result->push_back(L'\n'); *has_new_line = true; } - else if ((node_type >= FIRST_PARSE_TOKEN_TYPE && node_type <= LAST_PARSE_TOKEN_TYPE) || node_type == parse_special_type_parse_error) + else if ((node_type >= FIRST_PARSE_TOKEN_TYPE && node_type <= LAST_PARSE_TOKEN_TYPE) || + node_type == parse_special_type_parse_error) { if (node.has_source()) { - /* Some type representing a particular token */ - append_whitespace(node_indent, do_indent, *has_new_line, out_result); + // Some type representing a particular token. + if (prev_node_type != parse_token_type_redirection) + { + append_whitespace(node_indent, do_indent, *has_new_line, out_result); + } out_result->append(source, node.source_start, node.source_length); *has_new_line = false; } } - /* Recurse to all our children */ + // Recurse to all our children. for (node_offset_t idx = 0; idx < node.child_count; idx++) { - /* Note we pass our type to our child, which becomes its parent node type */ - prettify_node_recursive(source, tree, node.child_start + idx, node_indent, node_type, has_new_line, out_result, do_indent); + // Note we pass our type to our child, which becomes its parent node type. + prettify_node_recursive(source, tree, node.child_start + idx, node_indent, node_type, + has_new_line, out_result, do_indent); } } @@ -297,9 +337,10 @@ int main(int argc, char *argv[]) } output_type = output_type_plain_text; bool do_indent = true; - const char *short_opts = "+hvi"; + const char *short_opts = "+dhvi"; const struct option long_opts[] = { + { "dump", no_argument, NULL, 'd' }, { "no-indent", no_argument, NULL, 'i' }, { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'v' }, @@ -319,6 +360,12 @@ int main(int argc, char *argv[]) exit_without_destructors(127); } + case 'd': + { + dump_parse_tree = true; + break; + } + case 'h': { print_help("fish_indent", 1); diff --git a/src/parse_constants.h b/src/parse_constants.h index e7c6399f3..afc88c6c2 100644 --- a/src/parse_constants.h +++ b/src/parse_constants.h @@ -11,6 +11,8 @@ #define PARSE_ASSERT(a) assert(a) #define PARSER_DIE() do { fprintf(stderr, "Parser dying!\n"); exit_without_destructors(-1); } while (0) +// IMPORTANT: If the following enum is modified you must update the corresponding parser_token_types +// array in parse_tree.cpp. enum parse_token_type_t { token_type_invalid, @@ -41,7 +43,7 @@ enum parse_token_type_t symbol_plain_statement, symbol_arguments_or_redirections_list, symbol_argument_or_redirection, - + symbol_andor_job_list, symbol_argument_list, @@ -80,6 +82,8 @@ enum parse_token_type_t FIRST_PARSE_TOKEN_TYPE = parse_token_type_string, LAST_PARSE_TOKEN_TYPE = parse_token_type_end } __packed; +// Array of strings corresponding to the enums above instantiated in parse_tree.cpp. +extern wcstring parser_token_types[]; /* These must be maintained in sorted order (except for none, which isn't a keyword). This enables us to do binary search. */ enum parse_keyword_t diff --git a/src/parse_tree.cpp b/src/parse_tree.cpp index 51ae77202..3d2f7b8c7 100644 --- a/src/parse_tree.cpp +++ b/src/parse_tree.cpp @@ -16,6 +16,49 @@ #include #include +// This array provides strings for each symbol in enum parse_token_type_t in parse_constants.h. +wcstring parser_token_types[] = { + L"token_type_invalid", + L"symbol_job_list", + L"symbol_job", + L"symbol_job_continuation", + L"symbol_statement", + L"symbol_block_statement", + L"symbol_block_header", + L"symbol_for_header", + L"symbol_while_header", + L"symbol_begin_header", + L"symbol_function_header", + L"symbol_if_statement", + L"symbol_if_clause", + L"symbol_else_clause", + L"symbol_else_continuation", + L"symbol_switch_statement", + L"symbol_case_item_list", + L"symbol_case_item", + L"symbol_boolean_statement", + L"symbol_decorated_statement", + L"symbol_plain_statement", + L"symbol_arguments_or_redirections_list", + L"symbol_argument_or_redirection", + L"symbol_andor_job_list", + L"symbol_argument_list", + L"symbol_freestanding_argument_list", + L"symbol_argument", + L"symbol_redirection", + L"symbol_optional_background", + L"symbol_end_command", + L"parse_token_type_string", + L"parse_token_type_pipe", + L"parse_token_type_redirection", + L"parse_token_type_background", + L"parse_token_type_end", + L"parse_token_type_terminate", + L"parse_special_type_parse_error", + L"parse_special_type_tokenizer_error", + L"parse_special_type_comment", + }; + using namespace parse_productions; static bool production_is_empty(const production_t *production) diff --git a/tests/indent.in b/tests/indent.in index 83ef915b3..a1143c676 100644 --- a/tests/indent.in +++ b/tests/indent.in @@ -83,3 +83,9 @@ echo -n ' if begin ; false; end; echo hi ; end while begin ; false; end; echo hi ; end ' | ../test/root/bin/fish_indent + +echo \nTest redir formatting +# issue 2899 +echo -n ' +echo < stdin >>appended yes 2>&1 no > stdout maybe 2>& 4 | cat 2>| cat +' | ../test/root/bin/fish_indent diff --git a/tests/indent.out b/tests/indent.out index b2eaf08a4..6b38a72fd 100644 --- a/tests/indent.out +++ b/tests/indent.out @@ -91,3 +91,7 @@ while begin end echo hi end + +Test redir formatting + +echo >appended yes 2>&1 no >stdout maybe 2>&4 | cat 2>| cat From 02f18cae0aa9c44beade38b1b14c9f59d54b9078 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Wed, 6 Apr 2016 13:04:44 -0700 Subject: [PATCH 066/363] fix setting of $argv for `source` w/no args Fixes #139 --- src/builtin.cpp | 2 +- tests/test1.in | 8 +++++++- tests/test1.out | 4 ++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/builtin.cpp b/src/builtin.cpp index dd67817a3..86213a762 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -3375,7 +3375,7 @@ static int builtin_source(parser_t &parser, io_streams_t &streams, wchar_t **arg parser.push_block(new source_block_t(fn_intern)); reader_push_current_filename(fn_intern); - env_set_argv((argc>2)?(argv+2):(argv+1)); + env_set_argv(argc > 1 ? argv + 2 : argv + 1); res = reader_read(fd, streams.io_chain ? *streams.io_chain : io_chain_t()); diff --git a/tests/test1.in b/tests/test1.in index d52d558ee..ff6075cc8 100644 --- a/tests/test1.in +++ b/tests/test1.in @@ -166,5 +166,11 @@ function always_fails end end -always_fails ; echo $status +# Verify $argv set correctly in sourced scripts. +# Issue #139 +echo 'echo "source argv {$argv}"' | source +echo 'echo "source argv {$argv}"' | source - +echo 'echo "source argv {$argv}"' | source - abc +echo 'echo "source argv {$argv}"' | source - abc def +always_fails ; echo $status diff --git a/tests/test1.out b/tests/test1.out index fb5ea8c8a..26251a3bf 100644 --- a/tests/test1.out +++ b/tests/test1.out @@ -52,4 +52,8 @@ pipe 11 pipe 12 Checking for infinite loops in no-execute before comment after comment +source argv {} +source argv {} +source argv {abc} +source argv {abc def} 1 From 35e282928ade1b46f9faec8d8bd04c6fe66eafcc Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Wed, 6 Apr 2016 17:18:06 -0700 Subject: [PATCH 067/363] clarify documentation for the `source` command Make it clear that fish 2.3.0 changed how `$argv` is initialized. --- doc_src/source.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc_src/source.txt b/doc_src/source.txt index dba14045c..abb99af5f 100644 --- a/doc_src/source.txt +++ b/doc_src/source.txt @@ -7,7 +7,7 @@ source FILENAME [ARGUMENTS...] \subsection source-description Description -`source` evaluates the commands of the specified file in the current shell. This is different from starting a new process to perform the commands (i.e. `fish < FILENAME`) since the commands will be evaluated by the current shell, which means that changes in shell variables will affect the current shell. If additional arguments are specified after the file name, they will be inserted into the $argv variable. +`source` evaluates the commands of the specified file in the current shell. This is different from starting a new process to perform the commands (i.e. `fish < FILENAME`) since the commands will be evaluated by the current shell, which means that changes in shell variables will affect the current shell. If additional arguments are specified after the file name, they will be inserted into the `$argv` variable. The `$argv` variable will not include the name of the sourced file. If no file is specified, or if the file name '`-`' is used, stdin will be read. @@ -22,3 +22,7 @@ The return status of `source` is the return status of the last job to execute. I source ~/.config/fish/config.fish # Causes fish to re-read its initialization file. \endfish + +\subsection Caveats + +In fish versions prior to 2.3.0 the `$argv` variable would have a single element (the name of the sourced file) if no arguments are present. Otherwise it would contain arguments without the name of the sourced file. That behavior was very confusing and unlike other shells such as bash and zsh. From c37c93fcf66d94ce8882cec186fc73e74f28870b Mon Sep 17 00:00:00 2001 From: David Adam Date: Wed, 6 Apr 2016 22:57:45 +0800 Subject: [PATCH 068/363] configure: remove tiny leftover from xsel build --- configure.ac | 2 -- 1 file changed, 2 deletions(-) diff --git a/configure.ac b/configure.ac index 72a210fe6..2a96f63be 100644 --- a/configure.ac +++ b/configure.ac @@ -15,8 +15,6 @@ AC_INIT(fish, m4_esyscmd([cut -f 3 -d ' ' FISH-BUILD-VERSION-FILE | tr -d '\n']), fish-users@lists.sourceforge.net) -conf_arg=$@ - # # List of output variables produced by this configure script # From e1e1e558ce3191dcbda451c8a344b0d56de00910 Mon Sep 17 00:00:00 2001 From: David Adam Date: Thu, 7 Apr 2016 11:07:44 +0800 Subject: [PATCH 069/363] pcre2: add maintainer mode and disable by default --- Makefile.in | 2 +- pcre2-10.20/Makefile.in | 9 +++++---- pcre2-10.20/aclocal.m4 | 36 ++++++++++++++++++++++++++++++++++++ pcre2-10.20/configure | 38 ++++++++++++++++++++++++++++++++++++++ pcre2-10.20/configure.ac | 5 +++++ 5 files changed, 85 insertions(+), 5 deletions(-) diff --git a/Makefile.in b/Makefile.in index ea008211a..341b9420d 100644 --- a/Makefile.in +++ b/Makefile.in @@ -821,7 +821,7 @@ $(PCRE2_LIB): $(PCRE2_H) $(MAKE) -C $(PCRE2_DIR) libpcre2-$(PCRE2_WIDTH).la $(PCRE2_H): - (cd $(PCRE2_DIR) && ./config.status --enable-maintainer-mode) + (cd $(PCRE2_DIR) && ./config.status) # # Build the fish_tests program. diff --git a/pcre2-10.20/Makefile.in b/pcre2-10.20/Makefile.in index 9c24e5a2e..defe5d688 100644 --- a/pcre2-10.20/Makefile.in +++ b/pcre2-10.20/Makefile.in @@ -708,6 +708,7 @@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ @@ -1197,7 +1198,7 @@ all: $(BUILT_SOURCES) .SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs am--refresh: Makefile @: -$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ @@ -1223,9 +1224,9 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) $(SHELL) ./config.status --recheck -$(top_srcdir)/configure: $(am__configure_deps) +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) $(am__cd) $(srcdir) && $(AUTOCONF) -$(ACLOCAL_M4): $(am__aclocal_m4_deps) +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) $(am__aclocal_m4_deps): @@ -1236,7 +1237,7 @@ src/config.h: src/stamp-h1 src/stamp-h1: $(top_srcdir)/src/config.h.in $(top_builddir)/config.status @rm -f src/stamp-h1 cd $(top_builddir) && $(SHELL) ./config.status src/config.h -$(top_srcdir)/src/config.h.in: $(am__configure_deps) +$(top_srcdir)/src/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) rm -f src/stamp-h1 touch $@ diff --git a/pcre2-10.20/aclocal.m4 b/pcre2-10.20/aclocal.m4 index d1a41f717..db6146ab0 100644 --- a/pcre2-10.20/aclocal.m4 +++ b/pcre2-10.20/aclocal.m4 @@ -920,6 +920,42 @@ fi rmdir .tst 2>/dev/null AC_SUBST([am__leading_dot])]) +# Add --enable-maintainer-mode option to configure. -*- Autoconf -*- +# From Jim Meyering + +# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_MAINTAINER_MODE([DEFAULT-MODE]) +# ---------------------------------- +# Control maintainer-specific portions of Makefiles. +# Default is to disable them, unless 'enable' is passed literally. +# For symmetry, 'disable' may be passed as well. Anyway, the user +# can override the default with the --enable/--disable switch. +AC_DEFUN([AM_MAINTAINER_MODE], +[m4_case(m4_default([$1], [disable]), + [enable], [m4_define([am_maintainer_other], [disable])], + [disable], [m4_define([am_maintainer_other], [enable])], + [m4_define([am_maintainer_other], [enable]) + m4_warn([syntax], [unexpected argument to AM@&t@_MAINTAINER_MODE: $1])]) +AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) + dnl maintainer-mode's default is 'disable' unless 'enable' is passed + AC_ARG_ENABLE([maintainer-mode], + [AS_HELP_STRING([--]am_maintainer_other[-maintainer-mode], + am_maintainer_other[ make rules and dependencies not useful + (and sometimes confusing) to the casual installer])], + [USE_MAINTAINER_MODE=$enableval], + [USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes])) + AC_MSG_RESULT([$USE_MAINTAINER_MODE]) + AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes]) + MAINT=$MAINTAINER_MODE_TRUE + AC_SUBST([MAINT])dnl +] +) + # Check to see how 'make' treats includes. -*- Autoconf -*- # Copyright (C) 2001-2014 Free Software Foundation, Inc. diff --git a/pcre2-10.20/configure b/pcre2-10.20/configure index 15777d450..21e553b3c 100755 --- a/pcre2-10.20/configure +++ b/pcre2-10.20/configure @@ -734,6 +734,9 @@ CFLAGS CC ac_ct_AR AR +MAINT +MAINTAINER_MODE_FALSE +MAINTAINER_MODE_TRUE AM_BACKSLASH AM_DEFAULT_VERBOSITY AM_DEFAULT_V @@ -803,6 +806,7 @@ ac_subst_files='' ac_user_opts=' enable_option_checking enable_silent_rules +enable_maintainer_mode enable_dependency_tracking enable_shared enable_static @@ -1481,6 +1485,9 @@ Optional Features: --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-silent-rules less verbose build output (undo: "make V=1") --disable-silent-rules verbose build output (undo: "make V=0") + --enable-maintainer-mode + enable make rules and dependencies not useful (and + sometimes confusing) to the casual installer --enable-dependency-tracking do not reject slow dependency extractors --disable-dependency-tracking @@ -3129,6 +3136,33 @@ AM_BACKSLASH='\' ac_config_headers="$ac_config_headers src/config.h" +# FISH PATCH +# Enable maintainer mode to avoid spurious rebuilds due to timestamps in git +# not being stored. Discussion in https://github.com/fish-shell/fish-shell/issues/2469 + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5 +$as_echo_n "checking whether to enable maintainer-specific portions of Makefiles... " >&6; } + # Check whether --enable-maintainer-mode was given. +if test "${enable_maintainer_mode+set}" = set; then : + enableval=$enable_maintainer_mode; USE_MAINTAINER_MODE=$enableval +else + USE_MAINTAINER_MODE=no +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_MAINTAINER_MODE" >&5 +$as_echo "$USE_MAINTAINER_MODE" >&6; } + if test $USE_MAINTAINER_MODE = yes; then + MAINTAINER_MODE_TRUE= + MAINTAINER_MODE_FALSE='#' +else + MAINTAINER_MODE_TRUE='#' + MAINTAINER_MODE_FALSE= +fi + + MAINT=$MAINTAINER_MODE_TRUE + + + # This is a new thing required to stop a warning from automake 1.12 DEPDIR="${am__leading_dot}deps" @@ -15968,6 +16002,10 @@ else am__EXEEXT_FALSE= fi +if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then + as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then as_fn_error $? "conditional \"AMDEP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/pcre2-10.20/configure.ac b/pcre2-10.20/configure.ac index 20f3937b6..ad6ed7fde 100644 --- a/pcre2-10.20/configure.ac +++ b/pcre2-10.20/configure.ac @@ -29,6 +29,11 @@ AM_INIT_AUTOMAKE([dist-bzip2 dist-zip]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) AC_CONFIG_HEADERS(src/config.h) +# FISH PATCH +# Enable maintainer mode to avoid spurious rebuilds due to timestamps in git +# not being stored. Discussion in https://github.com/fish-shell/fish-shell/issues/2469 +AM_MAINTAINER_MODE + # This is a new thing required to stop a warning from automake 1.12 m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) From 9c755467240d7d2bbfb6a9cdb5e34d411ec9c99b Mon Sep 17 00:00:00 2001 From: David Adam Date: Thu, 7 Apr 2016 11:13:38 +0800 Subject: [PATCH 070/363] Update osx/config.h for removed ifdefs --- osx/config.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/osx/config.h b/osx/config.h index cab160a34..0a5530168 100644 --- a/osx/config.h +++ b/osx/config.h @@ -197,9 +197,6 @@ /* Define to 1 if the winsize struct and TIOCGWINSZ macro exist */ #define HAVE_WINSIZE 1 -/* Define to 1 if getopt_long exists and works. */ -#define HAVE_WORKING_GETOPT_LONG 1 - /* Define to 1 if the _nl_msg_cat_cntr symbol is exported. */ /* #undef HAVE__NL_MSG_CAT_CNTR */ From 8477126ae4c55d41adb9153f4632c09fffed4626 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 7 Apr 2016 18:26:10 +0200 Subject: [PATCH 071/363] Correct true-color statement in set_color docs - OSX Terminal does not support it - We do some detection --- doc_src/set_color.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc_src/set_color.txt b/doc_src/set_color.txt index 087c9ac96..d08373ca6 100644 --- a/doc_src/set_color.txt +++ b/doc_src/set_color.txt @@ -44,6 +44,6 @@ set_color normal; echo "Normal is nice" # This will reset background, too Fish uses a heuristic to decide if your terminal supports the 256 color palette (as opposed to the more limited 16 color palette of older terminals). If you've done the equivalent of `set fish_term256 1` that will be true. If the $TERM value contains "256color" (e.g., "xterm-256color") that will be true. If your $TERM value is "xterm" and $TERM_PROGRAM is not set to "Apple_Terminal" that will be true. If your terminal supports the full 256 color palette (which is pretty much every color terminal emulator written in the past decade) you should ensure one of the aforementioned conditions is true. -Many terminals support 24-bit (i.e., true-color) color escape sequences. This includes modern xterms, Gnome Terminal, KDE Konsole, and OS X Terminal and iTerm2. Fish does not currently auto-detect whether a given `$TERM` supports 24-bit colors. You can explicitly enable that support via `set fish_term24bit 1`. If you do so fish will not map your RGB color values to the closest known matching color in the ANSI X3.64 color palette. +Many terminals support 24-bit (i.e., true-color) color escape sequences. This includes modern xterms, Gnome Terminal, KDE Konsole, and iTerm2. Fish currently does some limited attempts to detect whether a given `$TERM` supports 24-bit colors. You can explicitly enable that support via `set fish_term24bit 1`. If you do so fish will not map your RGB color values to the closest known matching color in the ANSI X3.64 color palette. The `set_color` command uses the terminfo database to look up how to change terminal colors on whatever terminal is in use. Some systems have old and incomplete terminfo databases, and may lack color information for terminals that support it. Fish will use the [ANSI X3.64](https://en.wikipedia.org/wiki/ANSI_escape_code) escape sequences if the terminfo definition says less than 256 colors are supported; otherwise it will use the terminfo definition. From d2ae00ca44b7ae185835d0e71de244da29f16547 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Thu, 7 Apr 2016 13:29:50 -0700 Subject: [PATCH 072/363] Remove a dead function bool_from_env_var --- src/env_universal_common.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/env_universal_common.cpp b/src/env_universal_common.cpp index 751136159..900a2d75a 100644 --- a/src/env_universal_common.cpp +++ b/src/env_universal_common.cpp @@ -1737,9 +1737,3 @@ bool universal_notifier_t::notification_fd_became_readable(int fd) { return false; } - -static bool bool_from_env_var(const char *name, bool default_value) -{ - const char *var = getenv(name); - return var ? from_string(var) : default_value; -} From 784b438d4a686d6ca317d83133c6827ff23f4453 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Thu, 7 Apr 2016 13:47:51 -0700 Subject: [PATCH 073/363] fix git command in lint.fish script --- build_tools/lint.fish | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build_tools/lint.fish b/build_tools/lint.fish index ef66d207b..7182322f0 100755 --- a/build_tools/lint.fish +++ b/build_tools/lint.fish @@ -35,14 +35,14 @@ else # We haven't been asked to lint all the source. If there are uncommitted # changes lint those, else lint the files in the most recent commit. set pending (git status --porcelain --short --untracked-files=all | sed -e 's/^ *//') - if count $pending > /dev/null + if set -q pending[1] # There are pending changes so lint those files. for arg in $pending set files $files (string split -m 1 ' ' $arg)[2] end else # No pending changes so lint the files in the most recent commit. - set files (git show --porcelain --name-only --pretty=oneline head | tail --lines=+2) + set files (git show --word-diff=porcelain --name-only --pretty=oneline head)[2..-1] end # Extract just the C/C++ files. From 9347630d1e3bbe6aac342ff88378be0e31feee83 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Thu, 7 Apr 2016 13:30:27 -0700 Subject: [PATCH 074/363] Update Xcode project for latest Xcode --- fish.xcodeproj/project.pbxproj | 2 +- fish.xcodeproj/xcshareddata/xcschemes/Makefile.xcscheme | 2 +- fish.xcodeproj/xcshareddata/xcschemes/base.xcscheme | 2 +- fish.xcodeproj/xcshareddata/xcschemes/fish.app.xcscheme | 2 +- fish.xcodeproj/xcshareddata/xcschemes/fish_tests.xcscheme | 2 +- fish.xcodeproj/xcshareddata/xcschemes/install_tree.xcscheme | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/fish.xcodeproj/project.pbxproj b/fish.xcodeproj/project.pbxproj index 6d2ca9028..2d85b93a8 100644 --- a/fish.xcodeproj/project.pbxproj +++ b/fish.xcodeproj/project.pbxproj @@ -980,7 +980,7 @@ D0A084F213B3AC130099B651 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0720; + LastUpgradeCheck = 0730; TargetAttributes = { D008D0C41BC58F8800841177 = { CreatedOnToolsVersion = 7.0.1; diff --git a/fish.xcodeproj/xcshareddata/xcschemes/Makefile.xcscheme b/fish.xcodeproj/xcshareddata/xcschemes/Makefile.xcscheme index 886f78842..ad48a255f 100644 --- a/fish.xcodeproj/xcshareddata/xcschemes/Makefile.xcscheme +++ b/fish.xcodeproj/xcshareddata/xcschemes/Makefile.xcscheme @@ -1,6 +1,6 @@ Date: Thu, 7 Apr 2016 15:44:56 -0700 Subject: [PATCH 075/363] Complain less about "bogus" PATH entries When determining the old path, get the existing value in any scope, not just the set scope. Also only complain about absolute paths: relative paths are expected to be invalid sometimes. --- src/builtin_set.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/builtin_set.cpp b/src/builtin_set.cpp index ec530599d..bd871b09d 100644 --- a/src/builtin_set.cpp +++ b/src/builtin_set.cpp @@ -64,16 +64,22 @@ static int my_env_set(const wchar_t *key, const wcstring_list_t &val, int scope, /* Fix for https://github.com/fish-shell/fish-shell/issues/199 . Return success if any path setting succeeds. */ bool any_success = false; - /* Don't bother validating (or complaining about) values that are already present */ + /* Don't bother validating (or complaining about) values that are already present. + When determining already-present values, use ENV_DEFAULT instead of the passed-in scope because in: + set -l PATH stuff $PATH + where we are temporarily shadowing a variable, we want to compare against the shadowed value, not the + (missing) local value. + Also don't bother to complain about relative paths, which don't start with /. + */ wcstring_list_t existing_values; - const env_var_t existing_variable = env_get_string(key, scope); + const env_var_t existing_variable = env_get_string(key, ENV_DEFAULT); if (! existing_variable.missing_or_empty()) tokenize_variable_array(existing_variable, existing_values); for (i=0; i< val.size() ; i++) { const wcstring &dir = val.at(i); - if (list_contains_string(existing_values, dir)) + if (!string_prefixes_string(L"/", dir) || list_contains_string(existing_values, dir)) { any_success = true; continue; From 790c7f80c7d0fa1c97380885201eb50a2abcce02 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Fri, 8 Apr 2016 10:18:58 +0800 Subject: [PATCH 076/363] Implement an --invert/-v for string match, like grep -v. Only lines that do not match the pattern are shown. --- doc_src/string.txt | 12 +++++- src/builtin_string.cpp | 89 ++++++++++++++++++++++++++---------------- tests/string.in | 25 ++++++++++++ tests/string.out | 17 ++++++++ 4 files changed, 107 insertions(+), 36 deletions(-) diff --git a/doc_src/string.txt b/doc_src/string.txt index bae21dc99..58de93e40 100644 --- a/doc_src/string.txt +++ b/doc_src/string.txt @@ -12,7 +12,7 @@ string trim [(-l | --left)] [(-r | --right)] [(-c | --chars CHARS)] [(-q | --quiet)] [STRING...] string escape [(-n | --no-quoted)] [STRING...] string match [(-a | --all)] [(-i | --ignore-case)] [(-r | --regex)] - [(-n | --index)] [(-q | --quiet)] PATTERN [STRING...] + [(-n | --index)] [(-q | --quiet)] [(-v | --invert)] PATTERN [STRING...] string replace [(-a | --all)] [(-i | --ignore-case)] [(-r | --regex)] [(-q | --quiet)] PATTERN REPLACEMENT [STRING...] \endfish @@ -44,7 +44,7 @@ The following subcommands are available: - `escape` escapes each STRING such that it can be passed back to `eval` to produce the original argument again. By default, all special characters are escaped, and quotes are used to simplify the output when possible. If `-n` or `--no-quote` is given, the simplifying quoted format is not used. Exit status: 0 if at least one string was escaped, or 1 otherwise. -- `match` tests each STRING against PATTERN and prints matching substrings. Only the first match for each STRING is reported unless `-a` or `--all` is given, in which case all matches are reported. Matching can be made case-insensitive with `-i` or `--ignore-case`. If `-n` or `--index` is given, each match is reported as a 1-based start position and a length. By default, PATTERN is interpreted as a glob pattern matched against each entire STRING argument. If `-r` or `--regex` is given, PATTERN is interpreted as a Perl-compatible regular expression. For a regular expression containing capturing groups, multiple items will be reported for each match, one for the entire match and one for each capturing group. Exit status: 0 if at least one match was found, or 1 otherwise. +- `match` tests each STRING against PATTERN and prints matching substrings. Only the first match for each STRING is reported unless `-a` or `--all` is given, in which case all matches are reported. Matching can be made case-insensitive with `-i` or `--ignore-case`. If `-n` or `--index` is given, each match is reported as a 1-based start position and a length. By default, PATTERN is interpreted as a glob pattern matched against each entire STRING argument. If `-r` or `--regex` is given, PATTERN is interpreted as a Perl-compatible regular expression. For a regular expression containing capturing groups, multiple items will be reported for each match, one for the entire match and one for each capturing group. If --invert or -v is used the selected lines will be only those which do not match the given glob pattern or regular expression. Exit status: 0 if at least one match was found, or 1 otherwise. - `replace` is similar to `match` but replaces non-overlapping matching substrings with a replacement string and prints the result. By default, PATTERN is treated as a literal substring to be matched. If `-r` or `--regex` is given, PATTERN is interpreted as a Perl-compatible regular expression, and REPLACEMENT can contain C-style escape sequences like `\t` as well as references to capturing groups by number or name as `$n` or `${n}`. Exit status: 0 if at least one replacement was performed, or 1 otherwise. @@ -120,6 +120,14 @@ The following subcommands are available: >_ echo 'ok?' | string match '*\\?' >_ ok? + +>_ string match -r -v "c.*[12]" {cat,dog}(seq 1 4) +dog1 +dog2 +cat3 +dog3 +cat4 +dog4 \endfish \subsection string-example-match-regex Match Regex Examples diff --git a/src/builtin_string.cpp b/src/builtin_string.cpp index 8911d1ab0..10508a710 100644 --- a/src/builtin_string.cpp +++ b/src/builtin_string.cpp @@ -304,9 +304,10 @@ struct match_options_t bool all; bool ignore_case; bool index; + bool invert_match; bool quiet; - match_options_t(): all(false), ignore_case(false), index(false), quiet(false) { } + match_options_t(): all(false), ignore_case(false), index(false), invert_match(false), quiet(false) { } }; class string_matcher_t @@ -328,17 +329,15 @@ public: class wildcard_matcher_t: public string_matcher_t { +private: wcstring wcpattern; - public: wildcard_matcher_t(const wchar_t * /*argv0*/, const wchar_t *pattern, const match_options_t &opts, io_streams_t &streams) - : string_matcher_t(opts, streams) + : string_matcher_t(opts, streams), wcpattern(parse_util_unescape_wildcards(pattern)) { - wcpattern = parse_util_unescape_wildcards(pattern); - if (opts.ignore_case) { - for (int i = 0; i < wcpattern.length(); i++) + for (size_t i = 0; i < wcpattern.length(); i++) { wcpattern[i] = towlower(wcpattern[i]); } @@ -352,10 +351,11 @@ public: // Note: --all is a no-op for glob matching since the pattern is always // matched against the entire argument bool match; + if (opts.ignore_case) { wcstring s = arg; - for (int i = 0; i < s.length(); i++) + for (size_t i = 0; i < s.length(); i++) { s[i] = towlower(s[i]); } @@ -365,13 +365,11 @@ public: { match = wildcard_match(arg, wcpattern, false); } - if (match) + if (match ^ opts.invert_match) { total_matched++; - } - if (!opts.quiet) - { - if (match) + + if (!opts.quiet) { if (opts.index) { @@ -458,42 +456,53 @@ class pcre2_matcher_t: public string_matcher_t // Return values: -1 = error, 0 = no match, 1 = match if (pcre2_rc == PCRE2_ERROR_NOMATCH) { - return 0; + if (opts.invert_match && !opts.quiet) + { + streams.out.append(arg); + streams.out.push_back(L'\n'); + } + + return opts.invert_match ? 1 : 0; } - if (pcre2_rc < 0) + else if (pcre2_rc < 0) { string_error(streams, _(L"%ls: Regular expression match error: %ls\n"), argv0, pcre2_strerror(pcre2_rc).c_str()); return -1; } - if (pcre2_rc == 0) + else if (pcre2_rc == 0) { // The output vector wasn't big enough. Should not happen. string_error(streams, _(L"%ls: Regular expression internal error\n"), argv0); return -1; } + + else if (opts.invert_match) + return 0; + PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(regex.match); + for (int j = 0; j < pcre2_rc; j++) { PCRE2_SIZE begin = ovector[2*j]; PCRE2_SIZE end = ovector[2*j + 1]; - if (!opts.quiet) + + if (begin != PCRE2_UNSET && end != PCRE2_UNSET && !opts.quiet) { - if (begin != PCRE2_UNSET && end != PCRE2_UNSET) + if (opts.index) { - if (opts.index) - { - streams.out.append_format(L"%lu %lu", (unsigned long)(begin + 1), (unsigned long)(end - begin)); - } - else if (end > begin) // may have end < begin if \K is used - { - streams.out.append(wcstring(&arg[begin], end - begin)); - } - streams.out.append(L'\n'); + streams.out.append_format(L"%lu %lu", (unsigned long)(begin + 1), (unsigned long)(end - begin)); } + else if (end > begin) // may have end < begin if \K is used + { + streams.out.append(wcstring(&arg[begin], end - begin)); + } + streams.out.push_back(L'\n'); } } - return 1; + + return opts.invert_match ? 0 : 1; + } public: @@ -525,7 +534,7 @@ public: // pcre2 match error return false; } - if (rc == 0) + else if (rc == 0) { // no match return true; @@ -533,6 +542,11 @@ public: matched++; total_matched++; + if (opts.invert_match) + { + return true; + } + // Report any additional matches PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(regex.match); while (opts.all || matched == 0) @@ -573,12 +587,13 @@ public: static int string_match(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv) { - const wchar_t *short_options = L"ainqr"; + const wchar_t *short_options = L"ainvqr"; const struct woption long_options[] = { { L"all", no_argument, 0, 'a'}, { L"ignore-case", no_argument, 0, 'i'}, { L"index", no_argument, 0, 'n'}, + { L"invert", no_argument, 0, 'v'}, { L"quiet", no_argument, 0, 'q'}, { L"regex", no_argument, 0, 'r'}, { 0, 0, 0, 0 } @@ -612,6 +627,10 @@ static int string_match(parser_t &parser, io_streams_t &streams, int argc, wchar opts.index = true; break; + case 'v': + opts.invert_match = true; + break; + case 'q': opts.quiet = true; break; @@ -750,7 +769,7 @@ class regex_replacer_t: public string_replacer_t compiled_regex_t regex; wcstring replacement; - wcstring interpret_escapes(const wchar_t *orig) + static wcstring interpret_escapes(const wchar_t *orig) { wcstring result; @@ -782,6 +801,7 @@ public: bool replace_matches(const wchar_t *arg) { + // A return value of true means all is well (even if no replacements // were performed), false indicates an unrecoverable error. if (regex.code == 0) @@ -1064,7 +1084,7 @@ static int string_split(parser_t &parser, io_streams_t &streams, int argc, wchar string_error(streams, BUILTIN_ERR_TOO_MANY_ARGUMENTS, argv[0]); return BUILTIN_STRING_ERROR; } - + wcstring_list_t splits; size_t arg_count = 0; wcstring storage; @@ -1091,9 +1111,9 @@ static int string_split(parser_t &parser, io_streams_t &streams, int argc, wchar // If we are from the right, split_about gave us reversed strings, in reversed order! if (right) { - for (size_t i=0; i < splits.size(); i++) + for (size_t j = 0; j < splits.size(); j++) { - std::reverse(splits[i].begin(), splits[i].end()); + std::reverse(splits[j].begin(), splits[j].end()); } std::reverse(splits.begin(), splits.end()); } @@ -1293,7 +1313,7 @@ static int string_trim(parser_t &parser, io_streams_t &streams, int argc, wchar_ string_error(streams, BUILTIN_ERR_TOO_MANY_ARGUMENTS, argv[0]); return BUILTIN_STRING_ERROR; } - + /* if neither left or right is specified, we do both */ if (! do_left && ! do_right) { @@ -1339,6 +1359,7 @@ static const struct string_subcommand const wchar_t *name; int (*handler)(parser_t &, io_streams_t &, int argc, wchar_t **argv); } + string_subcommands[] = { { L"escape", &string_escape }, diff --git a/tests/string.in b/tests/string.in index 8afc97ff5..ac858618d 100644 --- a/tests/string.in +++ b/tests/string.in @@ -1,5 +1,26 @@ # tests for string builtin # mostly taken from examples + +string match -r -v "c.*" dog can cat diz; and echo "exit 0" + +string match -q -r -v "c.*" dog can cat diz; and echo "exit 0" + +string match -v "c*" dog can cat diz; and echo "exit 0" + +string match -q -v "c*" dog can cat diz; and echo "exit 0" + +string match -v "d*" dog dan dat diz; or echo "exit 1" + +string match -q -v "d*" dog dan dat diz; or echo "exit 1" + +string match -r -v x y; and echo "exit 0" + +string match -r -v x x; or echo "exit 1" + +string match -q -r -v x y; and echo "exit 0" + +string match -q -r -v x x; or echo "exit 1" + string length 'hello, world' string length -q ""; and echo not zero length @@ -63,3 +84,7 @@ string match -r '[' 'a[sd' 2>/dev/null; or echo "invalid expression error" string invalidarg 2>/dev/null; or echo "invalid argument error" string length 2>/dev/null; or echo "missing argument returns 0" + +string match -r -v "[dcantg].*" dog can cat diz; or echo "no regexp invert match" + +string match -v "???" dog can cat diz; or echo "no glob invert match" diff --git a/tests/string.out b/tests/string.out index 45ccb3692..bd3fff457 100644 --- a/tests/string.out +++ b/tests/string.out @@ -1,3 +1,18 @@ +dog +diz +exit 0 +exit 0 +dog +diz +exit 0 +exit 0 +exit 1 +exit 1 +y +exit 0 +exit 1 +exit 0 +exit 1 12 ab bc @@ -45,3 +60,5 @@ aabb invalid expression error invalid argument error missing argument returns 0 +no regexp invert match +no glob invert match From 36691df6fe4667645b71b550ce5eea90762ac23a Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Fri, 8 Apr 2016 10:46:51 +0800 Subject: [PATCH 077/363] Stringify many completions and functions, with --invert stringification. I believe apm must have been buggy - example output that I found online showed `tr` was mangling paths with spaces in it. Should be fixed. Also, use dscl on OS X in __fish_complete_users.fish like __fish_print_users.fish already does. --- share/completions/apm.fish | 4 ++-- share/completions/git.fish | 6 +++--- share/completions/gpg.fish | 2 ++ share/completions/npm.fish | 2 +- share/completions/rustc.fish | 8 ++++---- share/completions/ssh.fish | 4 ++-- share/functions/__fish_complete_pids.fish | 4 ++-- share/functions/__fish_complete_users.fish | 6 ++++-- share/functions/__fish_complete_wvdial_peers.fish | 6 +++--- share/functions/__fish_git_prompt.fish | 4 ++-- share/functions/__fish_paginate.fish | 2 +- share/functions/__fish_print_abook_emails.fish | 2 +- share/functions/__fish_print_hostnames.fish | 4 ++-- share/functions/__fish_print_pacman_repos.fish | 2 +- share/functions/__fish_print_users.fish | 4 ++-- share/tools/web_config/sample_prompts/acidhub.fish | 2 +- share/tools/web_config/sample_prompts/nim.fish | 4 ++-- 17 files changed, 35 insertions(+), 31 deletions(-) diff --git a/share/completions/apm.fish b/share/completions/apm.fish index 6b80a1b81..f9bf2f97a 100644 --- a/share/completions/apm.fish +++ b/share/completions/apm.fish @@ -61,7 +61,7 @@ end # Lists all apm config items function __fish_apm_config_items - apm config list | grep -v '^;\|^$' | tr "\ =\ " "\t" + apm config list | string match -r -v "^\s*;|^\s*\$" | string replace "\w*=\w*" \t end # Lists all installed atom packages @@ -69,7 +69,7 @@ function __fish_apm_list_packages apm list -b end -if apm -h ^| grep -q "Atom Package Manager" +if apm -h ^| string match -q "Atom Package Manager*" # Completions for Atom Package Manager ################## diff --git a/share/completions/git.fish b/share/completions/git.fish index 8ffdb071c..82ea0e8bf 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -11,13 +11,13 @@ function __fish_git_commits end function __fish_git_branches - command git branch --no-color -a ^/dev/null | __fish_sgrep -v ' -> ' | string trim -c "* " | string replace -r "^remotes/" "" + command git branch --no-color -a ^/dev/null | string match -r -v ' -> ' | string trim -c "* " | string replace -r "^remotes/" "" end function __fish_git_unique_remote_branches # Allow all remote branches with one remote without the remote part # This is useful for `git checkout` to automatically create a remote-tracking branch - command git branch --no-color -a ^/dev/null | __fish_sgrep -v ' -> ' | string trim -c "* " | string replace -r "^remotes/[^/]*/" "" | sort | uniq -u + command git branch --no-color -a ^/dev/null | string match -r -v ' -> ' | string trim -c "* " | string replace -r "^remotes/[^/]*/" "" | sort | uniq -u end function __fish_git_tags @@ -473,7 +473,7 @@ complete -f -c git -n '__fish_git_using_command status' -l ignore-submodules -x ### tag complete -f -c git -n '__fish_git_needs_command' -a tag -d 'Create, list, delete or verify a tag object signed with GPG' -complete -f -c git -n '__fish_git_using_command tag; and __fish_not_contain_opt -s d; and __fish_not_contain_opt -s v; and test (count (commandline -opc | __fish_sgrep -v -e \'^-\')) -eq 3' -a '(__fish_git_branches)' -d 'Branch' +complete -f -c git -n '__fish_git_using_command tag; and __fish_not_contain_opt -s d; and __fish_not_contain_opt -s v; and test (count (commandline -opc | string match -r -v \'^-\')) -eq 3' -a '(__fish_git_branches)' -d 'Branch' complete -f -c git -n '__fish_git_using_command tag' -s a -l annotate -d 'Make an unsigned, annotated tag object' complete -f -c git -n '__fish_git_using_command tag' -s s -l sign -d 'Make a GPG-signed tag' complete -f -c git -n '__fish_git_using_command tag' -s d -l delete -d 'Remove a tag' diff --git a/share/completions/gpg.fish b/share/completions/gpg.fish index 4e9d19104..9c236e654 100644 --- a/share/completions/gpg.fish +++ b/share/completions/gpg.fish @@ -47,6 +47,8 @@ function __fish_print_gpg_algo -d "Complete using all algorithms of the type spe # expire when the function goes out of scope, and the original locale # will take effect again. set -lx LC_ALL C + + # XXX this misses certain ciphers in gpg --version - redo this entirely and use fish's annoying group printing as a feature finally! gpg --version | __fish_sgrep "$argv:"| __fish_sgrep -v "Home:"|cut -d : -f 2 |tr , \n|tr -d " " end diff --git a/share/completions/npm.fish b/share/completions/npm.fish index 05393b858..534967ad0 100644 --- a/share/completions/npm.fish +++ b/share/completions/npm.fish @@ -60,7 +60,7 @@ complete -f -c npm -n 'not __fish_npm_needs_option' -a "(__fish_complete_npm)" # list available npm scripts and their parial content function __fish_npm_run - command npm run | command grep -v '^[^ ]' | command grep -v '^$' | command sed "s/^ *//" | while read -l name + command npm run | string match -r -v '^[^ ]|^$' | string trim | while read -l name set -l trim 20 read -l value echo "$value" | cut -c1-$trim | read -l value diff --git a/share/completions/rustc.fish b/share/completions/rustc.fish index d5caa03ca..8dadcaf67 100644 --- a/share/completions/rustc.fish +++ b/share/completions/rustc.fish @@ -49,13 +49,13 @@ for line in $rust_docs end set -l rust_docs (rustc -W help \ - | egrep \ - '(\s+)(.+)(\s+)(allow|warn|deny|forbid)(\s+){2}([^\n]+)' \ + | string match -r \ + '(?:\s+)(?:.+)(?:\s+)(?:allow|warn|deny|forbid)(?:\s+){2}(?:[^\n]+)' \ | string replace -r -i \ '(\s+)(.+)(\s+)(allow|warn|deny|forbid)(\s+){2}([^\n]+)' '$2 $6' \ | string match -r '^.*[^:]$' \ - | egrep -v '^(allow|warn|deny|forbid)$' \ - | egrep -v '^([a-z\-]+)(\s+)(allow|warn|deny|forbid)') + | string match -r -v '^(allow|warn|deny|forbid)$' \ + | string match -r -v '^([a-z\-]+)(\s+)(allow|warn|deny|forbid)') for line in $rust_docs set docs (string split -m 1 ' ' $line) diff --git a/share/completions/ssh.fish b/share/completions/ssh.fish index 896b542ed..6131c2545 100644 --- a/share/completions/ssh.fish +++ b/share/completions/ssh.fish @@ -15,7 +15,7 @@ complete -x -c ssh -d Hostname -a " " complete -x -c ssh -d User -a " -(__fish_print_users | __fish_sgrep -v '^_')@ +(__fish_print_users | string match -r -v '^_')@ " complete -c ssh --description "Command to run" -x -a '(__fish_complete_subcommand --fcs-skip=2)' @@ -23,7 +23,7 @@ complete -c ssh -s a --description "Disables forwarding of the authentication ag complete -c ssh -s A --description "Enables forwarding of the authentication agent" complete -x -c ssh -s b --description "Interface to transmit from" -a " ( - cat /proc/net/arp ^/dev/null| __fish_sgrep -v '^IP'|cut -d ' ' -f 1 ^/dev/null + cat /proc/net/arp ^/dev/null| string match -r -v '^IP'|cut -d ' ' -f 1 ^/dev/null ) " diff --git a/share/functions/__fish_complete_pids.fish b/share/functions/__fish_complete_pids.fish index 65ba47946..e974ed61f 100644 --- a/share/functions/__fish_complete_pids.fish +++ b/share/functions/__fish_complete_pids.fish @@ -1,11 +1,11 @@ function __fish_complete_pids -d "Print a list of process identifiers along with brief descriptions" # This may be a bit slower, but it's nice - having the tty displayed is really handy # 'tail -n +2' deletes the first line, which contains the headers - # 'grep -v...' removes self from the output + # %self is removed from output by string match -r -v set -l SELF %self # Display the tty if available # But not if it's just question marks, meaning no tty - ps axc -o pid,ucomm,tty | grep -v '^\s*'$SELF'\s' | tail -n +2 | string replace -r ' *([0-9]+) +([^ ].*[^ ]|[^ ]) +([^ ]+) *$' '$1\t$2 [$3]' | string replace -r ' *\[\?*\] *$' '' + ps axc -o pid,ucomm,tty | string match -r -v '^\s*'$SELF'\s' | tail -n +2 | string replace -r ' *([0-9]+) +([^ ].*[^ ]|[^ ]) +([^ ]+) *$' '$1\t$2 [$3]' | string replace -r ' *\[\?*\] *$' '' end diff --git a/share/functions/__fish_complete_users.fish b/share/functions/__fish_complete_users.fish index 91c254daa..199231be1 100644 --- a/share/functions/__fish_complete_users.fish +++ b/share/functions/__fish_complete_users.fish @@ -1,8 +1,10 @@ function __fish_complete_users --description "Print a list of local users, with the real user name as a description" if test -x /usr/bin/getent - getent passwd | cut -d : -f 1,5 | sed 's/:/\t/' + getent passwd | cut -d : -f 1,5 | string replace -r ':' \t + else if test -x /usr/bin/dscl + dscl . -list /Users RealName | string match -r -v '^_' | string replace -r ' {2,}' \t else - __fish_sgrep -ve '^#' /etc/passwd | cut -d : -f 1,5 | sed 's/:/\t/' + string match -v -r '^\s*#' < /etc/passwd | cut -d : -f 1,5 | string replace ':' \t end end diff --git a/share/functions/__fish_complete_wvdial_peers.fish b/share/functions/__fish_complete_wvdial_peers.fish index 11155b1e2..5eab088bc 100644 --- a/share/functions/__fish_complete_wvdial_peers.fish +++ b/share/functions/__fish_complete_wvdial_peers.fish @@ -15,15 +15,15 @@ function __fish_complete_wvdial_peers --description 'Complete wvdial peers' --ar case -C --config set store_next true case '--config=*' - set cfgfiles (echo $opt | sed 's/--config=//') + set cfgfiles (echo $opt | string replace '--config=' '') end end for file in $cfgfiles if test -f $file - cat $file | grep '\[Dialer' | sed 's/\[Dialer \(.\+\)\]/\1/' + string match -r '\[Dialer' < $file | string replace -r '\[Dialer (.+)\]' '$1' end - end | sort -u | grep -v Defaults + end | sort -u | string match -v Defaults end diff --git a/share/functions/__fish_git_prompt.fish b/share/functions/__fish_git_prompt.fish index 4fb2f16c8..128a3f975 100644 --- a/share/functions/__fish_git_prompt.fish +++ b/share/functions/__fish_git_prompt.fish @@ -286,8 +286,8 @@ function __fish_git_prompt_show_upstream --description "Helper function for __fi set -l os set -l commits (command git rev-list --left-right $upstream...HEAD ^/dev/null; set os $status) if test $os -eq 0 - set -l behind (count (for arg in $commits; echo $arg; end | grep '^<')) - set -l ahead (count (for arg in $commits; echo $arg; end | grep -v '^<')) + set -l behind (count (for arg in $commits; echo $arg; end | string match -r '^<')) + set -l ahead (count (for arg in $commits; echo $arg; end | string match -r -v '^<')) set count "$behind $ahead" else set count diff --git a/share/functions/__fish_paginate.fish b/share/functions/__fish_paginate.fish index 7887cf6b2..31cdd61fa 100644 --- a/share/functions/__fish_paginate.fish +++ b/share/functions/__fish_paginate.fish @@ -5,7 +5,7 @@ function __fish_paginate -d "Paginate the current command using the users defaul set cmd $PAGER end - if commandline -j|grep -v "$cmd *\$" >/dev/null + if commandline -j| string match -q -r -v "$cmd *\$" commandline -aj " ^&1 |$cmd;" end diff --git a/share/functions/__fish_print_abook_emails.fish b/share/functions/__fish_print_abook_emails.fish index c1a8d294e..f32b2aed6 100644 --- a/share/functions/__fish_print_abook_emails.fish +++ b/share/functions/__fish_print_abook_emails.fish @@ -1,4 +1,4 @@ function __fish_print_abook_emails --description 'Print email addresses (abook)' - abook --mutt-query "" | egrep -v '^\s*$' + abook --mutt-query "" | string match -r -v '^\s*$' end diff --git a/share/functions/__fish_print_hostnames.fish b/share/functions/__fish_print_hostnames.fish index 385157768..40499d1e7 100644 --- a/share/functions/__fish_print_hostnames.fish +++ b/share/functions/__fish_print_hostnames.fish @@ -5,11 +5,11 @@ function __fish_print_hostnames -d "Print a list of known hostnames" # Print all hosts from /etc/hosts if type -q getent # Ignore zero ips - getent hosts | grep -v '^0.0.0.0' \ + getent hosts | string match -r -v '^0.0.0.0' \ | string replace -r '[0-9.]*\s*' '' | string split " " else if test -r /etc/hosts # Ignore commented lines and functionally empty lines - grep -v '^\s*0.0.0.0\|^\s*#\|^\s*$' /etc/hosts \ + string match -r -v '^\s*0.0.0.0|^\s*#|^\s*$' < /etc/hosts \ # Strip comments | string replace -ra '#.*$' '' \ | string replace -r '[0-9.]*\s*' '' | string trim | string replace -ra '\s+' '\n' diff --git a/share/functions/__fish_print_pacman_repos.fish b/share/functions/__fish_print_pacman_repos.fish index 81fe3de5d..1998f7216 100644 --- a/share/functions/__fish_print_pacman_repos.fish +++ b/share/functions/__fish_print_pacman_repos.fish @@ -1,3 +1,3 @@ function __fish_print_pacman_repos --description "Print the repositories configured for arch's pacman package manager" - sed -n -e 's/\[\(.\+\)\]/\1/p' /etc/pacman.conf | grep -v "#\|options" + string replace -r -a "\[(.+)\]" "\1" < /etc/pacman.conf | string match -r -v "^#|options" end diff --git a/share/functions/__fish_print_users.fish b/share/functions/__fish_print_users.fish index f6d41fa93..d2915760f 100644 --- a/share/functions/__fish_print_users.fish +++ b/share/functions/__fish_print_users.fish @@ -3,9 +3,9 @@ function __fish_print_users --description "Print a list of local users" if test -x /usr/bin/getent getent passwd | cut -d : -f 1 else if test -x /usr/bin/dscl # OS X support - dscl . -list /Users | __fish_sgrep -v '^_' + dscl . -list /Users | string match -r -v '^_' else - __fish_sgrep -ve '^#' /etc/passwd | cut -d : -f 1 + string match -v -r '^\w*#' < /etc/passwd | cut -d : -f 1 end end diff --git a/share/tools/web_config/sample_prompts/acidhub.fish b/share/tools/web_config/sample_prompts/acidhub.fish index 45a7223e4..afb702463 100644 --- a/share/tools/web_config/sample_prompts/acidhub.fish +++ b/share/tools/web_config/sample_prompts/acidhub.fish @@ -12,7 +12,7 @@ function fish_prompt -d "Write out the prompt" if [ (_git_branch_name) ] set -l git_branch (set_color -o blue)(_git_branch_name) if [ (_is_git_dirty) ] - for i in (git branch -qv --no-color|grep \*|cut -d' ' -f4-|cut -d] -f1|tr , \n)\ + for i in (git branch -qv --no-color| string match -r \*|cut -d' ' -f4-|cut -d] -f1|tr , \n)\ (git status --porcelain | cut -c 1-2 | uniq) switch $i case "*[ahead *" diff --git a/share/tools/web_config/sample_prompts/nim.fish b/share/tools/web_config/sample_prompts/nim.fish index 347af1d4d..5d2dac394 100644 --- a/share/tools/web_config/sample_prompts/nim.fish +++ b/share/tools/web_config/sample_prompts/nim.fish @@ -3,7 +3,7 @@ function fish_prompt and set retc green; or set retc red - tty|grep -q tty; and set tty tty; or set tty pts + tty|string match -q -r tty; and set tty tty; or set tty pts set_color $retc if [ $tty = tty ] @@ -48,7 +48,7 @@ function fish_prompt echo -n ] if type -q acpi - if [ (acpi -a 2> /dev/null | grep off) ] + if [ (acpi -a 2> /dev/null | string match -r off) ] echo -n '─[' set_color -o red echo -n (acpi -b|cut -d' ' -f 4-) From e395a0eb6927f298fcc7d0764ed27605941f34c0 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Thu, 7 Apr 2016 15:24:52 -0700 Subject: [PATCH 078/363] Migrate PATH-completion logic from complete.cpp to expand.cpp Prior to this fix, when completing a command that doesn't have a /, we would prepend each component of PATH and then expand the whole thing. So any special characters in the PATH would be interpreted when performing tab completion. With this fix, we pull the PATH resolution out of complete.cpp and migrate it to expand.cpp. This unifies nicely with the CDPATH resolution already in that file. This requires introducing a new expand flag EXPAND_SPECIAL_FOR_COMMAND, which is analogous to EXPAND_SPECIAL_CD (which is renamed to EXPAND_SPECIAL_FOR_CD). This flag tells expand to resolve paths against PATH instead of the working directory. Fixes #952 --- src/complete.cpp | 49 ++---------------------------------------------- src/expand.cpp | 41 +++++++++++++++++++++++++--------------- src/expand.h | 8 ++++++-- src/wildcard.cpp | 8 ++++---- tests/test6.in | 34 +++++++++++++++++++++++++++++++++ tests/test6.out | 2 ++ 6 files changed, 74 insertions(+), 68 deletions(-) diff --git a/src/complete.cpp b/src/complete.cpp index 8af2c822f..5c817b8d0 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -851,8 +851,7 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool if (use_command) { - - if (expand_string(str_cmd, &this->completions, EXPAND_FOR_COMPLETIONS | EXECUTABLES_ONLY | this->expand_flags(), NULL) != EXPAND_ERROR) + if (expand_string(str_cmd, &this->completions, EXPAND_SPECIAL_FOR_COMMAND | EXPAND_FOR_COMPLETIONS | EXECUTABLES_ONLY | this->expand_flags(), NULL) != EXPAND_ERROR) { if (this->wants_descriptions()) { @@ -869,52 +868,8 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool } if (str_cmd.find(L'/') == wcstring::npos && str_cmd.at(0) != L'~') { - if (use_command) - { - - const env_var_t path = this->vars.get(L"PATH"); - if (!path.missing()) - { - wcstring base_path; - wcstokenizer tokenizer(path, ARRAY_SEP_STR); - while (tokenizer.next(base_path)) - { - if (base_path.empty()) - continue; - - /* Make sure the base path ends with a slash */ - if (base_path.at(base_path.size() - 1) != L'/') - base_path.push_back(L'/'); - - wcstring nxt_completion = base_path; - nxt_completion.append(str_cmd); - - size_t prev_count = this->completions.size(); - expand_flags_t expand_flags = EXPAND_FOR_COMPLETIONS | EXECUTABLES_ONLY | EXPAND_NO_FUZZY_DIRECTORIES | this->expand_flags(); - if (expand_string(nxt_completion, - &this->completions, - expand_flags, NULL) != EXPAND_ERROR) - { - /* For all new completions, if COMPLETE_REPLACES_TOKEN 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_REPLACES_TOKEN) - { - - c.completion.erase(0, base_path.size()); - } - } - } - } - if (this->wants_descriptions()) - this->complete_cmd_desc(str_cmd); - } - } - 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++) { @@ -1361,7 +1316,7 @@ void completer_t::complete_param_expand(const wcstring &str, bool do_file, bool if (handle_as_special_cd && do_file) { - flags |= DIRECTORIES_ONLY | EXPAND_SPECIAL_CD | EXPAND_NO_DESCRIPTIONS; + flags |= DIRECTORIES_ONLY | EXPAND_SPECIAL_FOR_CD | EXPAND_NO_DESCRIPTIONS; } /* Squelch file descriptions per issue 254 */ diff --git a/src/expand.cpp b/src/expand.cpp index de67ffa84..cb68eb980 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -1800,36 +1800,47 @@ static expand_error_t expand_stage_wildcards(const wcstring &input, std::vector< const wcstring working_dir = env_get_pwd_slash(); wcstring_list_t effective_working_dirs; - if (! (flags & EXPAND_SPECIAL_CD)) + bool for_cd = !!(flags & EXPAND_SPECIAL_FOR_CD); + bool for_command = !!(flags & EXPAND_SPECIAL_FOR_COMMAND); + if (!for_cd && !for_command) { /* Common case */ effective_working_dirs.push_back(working_dir); } else { - /* Ignore the CDPATH if we start with ./ or / */ - if (string_prefixes_string(L"./", path_to_expand)) - { - effective_working_dirs.push_back(working_dir); - } - else if (string_prefixes_string(L"/", path_to_expand)) + /* Either EXPAND_SPECIAL_FOR_COMMAND or EXPAND_SPECIAL_FOR_CD. We can handle these mostly the same. + There's the following differences: + 1. An empty CDPATH should be treated as '.', but an empty PATH should be left empty (no commands can be found). + 2. PATH is only "one level," while CDPATH is multiple levels. That is, input like 'foo/bar' should resolve + against CDPATH, but not PATH. + + In either case, we ignore the path if we start with ./ or /. + Also ignore it if we are doing command completion and we contain a slash, per IEEE 1003.1, chapter 8 under PATH + */ + if (string_prefixes_string(L"./", path_to_expand) || + string_prefixes_string(L"/", path_to_expand) || + (for_command && path_to_expand.find(L'/') != wcstring::npos)) { effective_working_dirs.push_back(working_dir); } else { - /* Get the CDPATH and cwd. Perhaps these should be passed in. */ - env_var_t cdpath = env_get_string(L"CDPATH"); - if (cdpath.missing_or_empty()) - cdpath = L"."; + /* Get the PATH/CDPATH and cwd. Perhaps these should be passed in. + An empty CDPATH implies just the current directory, while an empty PATH is left empty. + */ + env_var_t paths = env_get_string(for_cd ? L"CDPATH" : L"PATH"); + if (paths.missing_or_empty()) + paths = for_cd ? L"." : L""; /* Tokenize it into directories */ - wcstokenizer tokenizer(cdpath, ARRAY_SEP_STR); - wcstring next_cd_path; - while (tokenizer.next(next_cd_path)) + wcstokenizer tokenizer(paths, ARRAY_SEP_STR); + wcstring next_path; + while (tokenizer.next(next_path)) { /* Ensure that we use the working directory for relative cdpaths like "." */ - effective_working_dirs.push_back(path_apply_working_directory(next_cd_path, working_dir)); + effective_working_dirs.push_back(path_apply_working_directory(next_path, working_dir)); + } } } diff --git a/src/expand.h b/src/expand.h index 6ae91cca3..fafbcd2ad 100644 --- a/src/expand.h +++ b/src/expand.h @@ -52,8 +52,12 @@ enum // Disallow directory abbreviations like /u/l/b for /usr/local/bin. Only // applicable if EXPAND_FUZZY_MATCH is set. EXPAND_NO_FUZZY_DIRECTORIES = 1 << 10, - // Do expansions specifically to support cd (CDPATH, etc). - EXPAND_SPECIAL_CD = 1 << 11 + // Do expansions specifically to support cd + // This means using CDPATH as a list of potential working directories + EXPAND_SPECIAL_FOR_CD = 1 << 11, + // Do expansions specifically to support external command completions. + // This means using PATH as a list of potential working directories + EXPAND_SPECIAL_FOR_COMMAND = 1 << 12 }; typedef int expand_flags_t; diff --git a/src/wildcard.cpp b/src/wildcard.cpp index db211818c..202b836d4 100644 --- a/src/wildcard.cpp +++ b/src/wildcard.cpp @@ -789,8 +789,8 @@ class wildcard_expander_t c.prepend_token_prefix(this->original_base); } - /* Hack. Implement EXPAND_SPECIAL_CD by descending the deepest unique hierarchy we can, and then appending any components to each new result. */ - if (flags & EXPAND_SPECIAL_CD) + /* Hack. Implement EXPAND_SPECIAL_FOR_CD by descending the deepest unique hierarchy we can, and then appending any components to each new result. */ + if (flags & EXPAND_SPECIAL_FOR_CD) { wcstring unique_hierarchy = this->descend_unique_hierarchy(abs_path); if (! unique_hierarchy.empty()) @@ -1138,8 +1138,8 @@ int wildcard_expand_string(const wcstring &wc, const wcstring &working_directory /* Fuzzy matching only if we're doing completions */ assert((flags & (EXPAND_FUZZY_MATCH | EXPAND_FOR_COMPLETIONS)) != EXPAND_FUZZY_MATCH); - /* EXPAND_SPECIAL_CD requires DIRECTORIES_ONLY and EXPAND_FOR_COMPLETIONS and EXPAND_NO_DESCRIPTIONS */ - assert(!(flags & EXPAND_SPECIAL_CD) || + /* EXPAND_SPECIAL_FOR_CD requires DIRECTORIES_ONLY and EXPAND_FOR_COMPLETIONS and EXPAND_NO_DESCRIPTIONS */ + assert(!(flags & EXPAND_SPECIAL_FOR_CD) || ((flags & DIRECTORIES_ONLY) && (flags & EXPAND_FOR_COMPLETIONS) && (flags & EXPAND_NO_DESCRIPTIONS))); /* Hackish fix for 1631. We are about to call c_str(), which will produce a string truncated at any embedded nulls. We could fix this by passing around the size, etc. However embedded nulls are never allowed in a filename, so we just check for them and return 0 (no matches) if there is an embedded null. */ diff --git a/tests/test6.in b/tests/test6.in index 252eb6068..0d6ef4878 100644 --- a/tests/test6.in +++ b/tests/test6.in @@ -72,3 +72,37 @@ if begin; rm -rf test6.tmp.dir; and mkdir test6.tmp.dir; end else echo "error: could not create temp environment" >&2 end + +# Test command expansion with parened PATHs (#952) +begin + set -l parened_path $PWD/'test6.tmp2.(paren).dir' + set -l parened_subpath $parened_path/subdir + if not begin + rm -rf $parened_path + and mkdir $parened_path + and mkdir $parened_subpath + and ln -s /bin/ls $parened_path/'__test6_(paren)_command' + and ln -s /bin/ls $parened_subpath/'__test6_subdir_(paren)_command' + end + echo "error: could not create command expansion temp environment" >&2 + end + + # Verify that we can expand commands when PATH has parens + set -l PATH $parened_path $PATH + set -l completed (complete -C__test6_ | cut -f 1 -d \t) + if test "$completed" = '__test6_(paren)_command' + echo "Command completion with parened PATHs test passed" + else + echo "Command completion with parened PATHs test failed. Expected __test6_(paren)_command, got $completed" >&2 + end + + # Verify that commands with intermediate slashes do NOT expand with respect to PATH + set -l completed (complete -Csubdir/__test6_subdir) + if test -z "$completed" + echo "Command completion with intermediate slashes passed" + else + echo "Command completion with intermediate slashes: should output nothing, instead got $completed" >&2 + end + + rm -rf $parened_path +end diff --git a/tests/test6.out b/tests/test6.out index 268b05eab..ca6e69066 100644 --- a/tests/test6.out +++ b/tests/test6.out @@ -33,3 +33,5 @@ CCCC: implicit cd complete works no implicit cd complete after 'command' PATH does not cause incorrect implicit cd +Command completion with parened PATHs test passed +Command completion with intermediate slashes passed From 2ff3a366752bf96ccc71bdc70ecc10f31de0f901 Mon Sep 17 00:00:00 2001 From: David Adam Date: Fri, 8 Apr 2016 12:39:15 +0800 Subject: [PATCH 079/363] CHANGELOG: move release_notes.html to a markdown document [ci skip] --- CHANGELOG.md | 156 ++++++++++++++++++++++++++++++++ release_notes.html | 219 --------------------------------------------- 2 files changed, 156 insertions(+), 219 deletions(-) create mode 100644 CHANGELOG.md delete mode 100644 release_notes.html diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..e42bd92e3 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,156 @@ +# fish 2.0.0 + +Significant Changes +------------------- + +* **Command substitutions now modify `$status` #547.** + Previously the exit status of command substitutions (like `(pwd)`) was ignored; however now it modifies $status. Furthermore, the `set` command now only sets $status on failure; it is untouched on success. This allows for the following pattern: + + ```sh + if set python_path (which python) + ... + end + ``` + Because set does not modify $status on success, the if branch effectively tests whether `which` succeeded, and if so, whether the `set` also succeeded. +* **Improvements to $PATH handling.** + * There is a new variable, `$fish_user_paths`, which can be set universally, and whose contents are appended to $PATH #527 + * /etc/paths and /etc/paths.d are now respected on OS X + * fish no longer modifies $PATH to find its own binaries +* **Long lines no longer use ellipsis for line breaks**, and copy and paste + should no longer include a newline even if the line was broken #300 +* **New syntax for index ranges** (sometimes known as "slices") #212 +* **fish now supports an `else if` statement** #134 +* **Process and pid completion now works on OS X** #129 +* **fish is now relocatable**, and no longer depends on compiled-in paths #125 +* **fish now supports a right prompt (RPROMPT)** through the fish_right_prompt function #80 +* **fish now uses posix_spawn instead of fork when possible**, which is much faster on BSD and OS X #11 + +Other Notable Fixes +------------------- + +* Updated VCS completions (darcs, cvs, svn, etc.) +* Avoid calling getcwd on the main thread, as it can hang #696 +* Control-D (forward delete) no longer stops at a period #667 +* Completions for many new commands +* fish now respects rxvt's unique keybindings #657 +* xsel is no longer built as part of fish. It will still be invoked if installed separately #633 +* __fish_filter_mime no longer spews #628 +* The --no-execute option to fish no longer falls over when reaching the end of a block #624 +* fish_config knows how to find fish even if it's not in the $PATH #621 +* A leading space now prevents writing to history, as is done in bash and zsh #615 +* Hitting enter after a backslash only goes to a new line if it is followed by whitespace or the end of the line #613 +* printf is now a builtin #611 +* Event handlers should no longer fire if signals are blocked #608 +* set_color is now a builtin #578 +* man page completions are now located in a new generated_completions directory, instead of your completions directory #576 +* tab now clears autosuggestions #561 +* tab completion from within a pair of quotes now attempts to "appropriate" the closing quote #552 +* $EDITOR can now be a list: for example, `set EDITOR gvim -f`) #541 +* `case` bodies are now indented #530 +* The profile switch `-p` no longer crashes #517 +* You can now control-C out of `read` #516 +* `umask` is now functional on OS X #515 +* Avoid calling getpwnam on the main thread, as it can hang #512 +* Alt-F or Alt-right-arrow (Option-F or option-right-arrow) now accepts one word of an autosuggestion #435 +* Setting fish as your login shell no longer kills OpenSUSE #367 +* Backslashes now join lines, instead of creating multiple commands #347 +* echo now implements the -e flag to interpret escapes #337 +* When the last token in the user's input contains capital letters, use its case in preference to that of the autosuggestion #335 +* Descriptions now have their own muted color #279 +* Wildcards beginning with a . (for example, `ls .*`) no longer match . and .. #270 +* Recursive wildcards now handle symlink loops #268 +* You can now delete history items from the fish_config web interface #250 +* The OS X build now weak links `wcsdup` and `wcscasecmp` #240 +* fish now saves and restores the process group, which prevents certain processes from being erroneously reported as stopped #197 +* funced now takes an editor option #187 +* Alternating row colors are available in fish pager through `fish_pager_color_secondary` #186 +* Universal variable values are now stored based on your MAC address, not your hostname #183 +* The caret ^ now only does a stderr redirection if it is the first character of a token, making git users happy #168 +* Autosuggestions will no longer cause line wrapping #167 +* Better handling of Unicode combining characters #155 +* fish SIGHUPs processes more often #138 +* fish no longer causes `sudo` to ask for a password every time +* fish behaves better under Midnight Commander #121 +* `set -e` no longer crashes #100 +* fish now will automatically import history from bash, if there is no fish history #66 +* Backslashed-newlines inside quoted strings now behave more intuitively #52 +* Tab titles should be shown correctly in iTerm2 #47 +* scp remote path completion now sometimes works #42 +* The `read` builtin no longer shows autosuggestions #29 +* Custom key bindings can now be set via the `fish_user_key_bindings` function #21 +* All Python scripts now run correctly under both Python 2 and Python 3 #14 +* The "accept autosuggestion" key can now be configured #19 +* Autosuggestions will no longer suggest invalid commands #6 + +--- + +# fishfish Beta r2 + +Bug Fixes +--------- + +* **Implicit cd** is back, for paths that start with one or two dots, a slash, or a tilde. +* **Overrides of default functions should be fixed.** The "internalized scripts" feature is disabled for now. +* **Disabled delayed suspend.** This is a strange job-control feature of BSD systems, including OS X. Disabling it frees up Control Y for other purposes; in particular, for yank, which now works on OS X. +* **fish_indent is fixed.** In particular, the `funced` and `funcsave` functions work again. +* A SIGTERM now ends the whole execution stack again (resolving #13). +* Bumped the __fish_config_interactive version number so the default fish_color_autosuggestion kicks in. +* fish_config better handles combined term256 and classic colors like "555 yellow". + +New Features +------------ + +* **A history builtin**, and associated interactive function that enables deleting history items. Example usage: + * Print all history items beginning with echo: `history --prefix echo` + * Print all history items containing foo: `history --contains foo` + * Interactively delete some items containing foo: `history --delete --contains foo` + +Credit to @siteshwar for implementation. Thanks @siteshwar! + +--- + +# fishfish Beta r1 + +## Scripting +* No changes! All existing fish scripts, config files, completions, etc. from trunk should continue to work. + +## New Features +* **Autosuggestions**. Think URL fields in browsers. When you type a command, fish will suggest the rest of the command after the cursor, in a muted gray when possible. You can accept the suggestion with the right arrow key or Ctrl-F. Suggestions come from command history, completions, and some custom code for cd; there's a lot of potential for improvement here. The suggestions are computed on a background pthread, so they never slow down your typing. The autosuggestion feature is incredible. I miss it dearly every time I use anything else. + +* **term256 support** where available, specifically modern xterms and OS X Lion. You can specify colors the old way ('set_color cyan') or by specifying RGB hex values ('set_color FF3333'); fish will pick the closest supported color. Some xterms do not advertise term256 support either in the $TERM or terminfo max_colors field, but nevertheless support it. For that reason, fish will default into using it on any xterm (but it can be disabled with an environment variable). + +* **Web-based configuration** page. There is a new function 'fish_config'. This spins up a simple Python web server and opens a browser window to it. From this web page, you can set your shell colors and view your functions, variables, and history; all changes apply immediately to all running shells. Eventually all configuration ought to be supported via this mechanism (but in addition to, not instead of, command line mechanisms). + +* **Man page completions**. There is a new function 'fish_update_completions'. This function reads all the man1 files from your manpath, removes the roff formatting, parses them to find the commands and options, and outputs fish completions into ~/.config/fish/completions. It won't overwrite existing completion files (except ones that it generated itself). + +## Programmatic Changes +* fish is now entirely in C++. I have no particular love for C++, but it provides a ready memory-model to replace halloc. We've made an effort to keep it to a sane and portable subset (no C++11, no boost, no going crazy with templates or smart pointers), but we do use the STL and a little tr1. +* halloc is entirely gone, replaced by normal C++ ownership semantics. If you don't know what halloc is, well, now you have two reasons to be happy. +* All the crufty C data structures are entirely gone. array_list_t, priority_queue_t, hash_table_t, string_buffer_t have been removed and replaced by STL equivalents like std::vector, std::map, and std::wstring. A lot of the string handling now uses std::wstring instead of wchar_t * +* fish now spawns pthreads for tasks like syntax highlighting that require blocking I/O. +* History has been completely rewritten. History files now use an extensible YAML-style syntax. History "merging" (multiple shells writing to the same history file) now works better. There is now a maximum history length of about 250k items (256 * 1024). +* The parser has been "instanced," so you can now create more than one. +* Total #LoC has shrunk slightly even with the new features. + +## Performance +* fish now runs syntax highlighting in a background thread, so typing commands is always responsive even on slow filesystems. +* echo, test, and pwd are now builtins, which eliminates many forks. +* The files in share/functions and share/completions now get 'internalized' into C strings that get compiled in with fish. This substantially reduces the number of files touched at startup. A consequence is that you cannot change these functions without recompiling, but often other functions depend on these "standard" functions, so changing them is perhaps not a good idea anyways. + +Here are some system call counts for launching and then exiting fish with the default configuration, on OS X. The first column is fish trunk, the next column is with our changes, and the last column is bash for comparison. This data was collected via dtrace. + + +
before after bash +
open 9 4 5 +
fork 28 14 0 +
stat 131 85 11 +
lstat 670 0 0 +
read 332 80 4 +
write 172 149 0 +
+ +The large number of forks relative to bash are due to fish's insanely expensive default prompt, which is unchanged in my version. If we switch to a prompt comparable to bash's (lame) default, the forks drop to 16 with trunk, 4 after our changes. + +The large reduction in lstat() numbers is due to fish no longer needing to call ttyname() on OS X. + +We've got some work to do to be as lean as bash, but we're on the right track. diff --git a/release_notes.html b/release_notes.html deleted file mode 100644 index 8f300c5ce..000000000 --- a/release_notes.html +++ /dev/null @@ -1,219 +0,0 @@ - -fishfish shell release notes - - -

Release Notes for fish 2.0.0

- -

Significant Changes

- -
    -
  • Command substitutions now modify $status #547. -

    -Previously the exit status of command substitutions (like (pwd)) was ignored; however now it modifies $status. Furthermore, the set command now only sets $status on failure; it is untouched on success. This allows for the following pattern: -

    -if set python_path (which python)
    -   ...
    -end
    -
    - -Because set does not modify $status on success, the if branch effectively tests whether which succeeded, and if so, whether the set also succeeded. - -

  • - -
  • Improvements to $PATH handling. -

    -

    • There is a new variable, $fish_user_paths, which can be set universally, and whose contents are appended to $PATH #527
    • -
    • /etc/paths and /etc/paths.d are now respected on OS X
    • -
    • fish no longer modifies $PATH to find its own binaries
    • -
  • -

    - - -
  • Long lines no longer use ellipsis for line breaks, and copy and paste should no longer include a newline even if the line was broken #300
  • - -
  • New syntax for index ranges (sometimes known as "slices") #212
  • - -
  • fish now supports an else if statement #134
  • - -
  • Process and pid completion now works on OS X #129
  • - -
  • fish is now relocatable, and no longer depends on compiled-in paths #125
  • - -
  • fish now supports a right prompt (RPROMPT) through the fish_right_prompt function #80
  • - -
  • fish now uses posix_spawn instead of fork when possible, which is much faster on BSD and OS X #11
  • - -
- -

Other Notable Fixes

-
    -
  • Updated VCS completions (darcs, cvs, svn, etc.)
  • -
  • Avoid calling getcwd on the main thread, as it can hang #696
  • -
  • Control-D (forward delete) no longer stops at a period #667
  • -
  • Completions for many new commands
  • -
  • fish now respects rxvt's unique keybindings #657
  • -
  • xsel is no longer built as part of fish. It will still be invoked if installed separately #633
  • -
  • __fish_filter_mime no longer spews #628
  • -
  • The --no-execute option to fish no longer falls over when reaching the end of a block #624
  • -
  • fish_config knows how to find fish even if it's not in the $PATH #621
  • -
  • A leading space now prevents writing to history, as is done in bash and zsh #615
  • -
  • Hitting enter after a backslash only goes to a new line if it is followed by whitespace or the end of the line #613
  • -
  • printf is now a builtin #611
  • -
  • Event handlers should no longer fire if signals are blocked #608
  • -
  • set_color is now a builtin #578
  • -
  • man page completions are now located in a new generated_completions directory, instead of your completions directory #576
  • -
  • tab now clears autosuggestions #561
  • -
  • tab completion from within a pair of quotes now attempts to "appropriate" the closing quote #552
  • -
  • $EDITOR can now be a list: for example, set EDITOR gvim -f) #541
  • -
  • case bodies are now indented #530
  • -
  • The profile switch -p no longer crashes #517
  • -
  • You can now control-C out of read #516
  • -
  • umask is now functional on OS X #515
  • -
  • Avoid calling getpwnam on the main thread, as it can hang #512
  • -
  • Alt-F or Alt-right-arrow (Option-F or option-right-arrow) now accepts one word of an autosuggestion #435
  • -
  • Setting fish as your login shell no longer kills OpenSUSE #367
  • -
  • Backslashes now join lines, instead of creating multiple commands #347
  • -
  • echo now implements the -e flag to interpret escapes #337
  • -
  • When the last token in the user's input contains capital letters, use its case in preference to that of the autosuggestion #335
  • -
  • Descriptions now have their own muted color #279
  • -
  • Wildcards beginning with a . (for example, ls .*) no longer match . and .. #270
  • -
  • Recursive wildcards now handle symlink loops #268
  • -
  • You can now delete history items from the fish_config web interface #250
  • -
  • The OS X build now weak links wcsdup and wcscasecmp #240
  • -
  • fish now saves and restores the process group, which prevents certain processes from being erroneously reported as stopped #197
  • -
  • funced now takes an editor option #187
  • -
  • Alternating row colors are available in fish pager through fish_pager_color_secondary #186
  • -
  • Universal variable values are now stored based on your MAC address, not your hostname #183
  • -
  • The caret ^ now only does a stderr redirection if it is the first character of a token, making git users happy #168
  • -
  • Autosuggestions will no longer cause line wrapping #167
  • -
  • Better handling of Unicode combining characters #155
  • -
  • fish SIGHUPs processes more often #138
  • -
  • fish no longer causes sudo to ask for a password every time
  • -
  • fish behaves better under Midnight Commander #121
  • -
  • set -e no longer crashes #100
  • -
  • fish now will automatically import history from bash, if there is no fish history #66
  • -
  • Backslashed-newlines inside quoted strings now behave more intuitively #52
  • -
  • Tab titles should be shown correctly in iTerm2 #47
  • -
  • scp remote path completion now sometimes works #42
  • -
  • The read builtin no longer shows autosuggestions #29
  • -
  • Custom key bindings can now be set via the fish_user_key_bindings function #21
  • -
  • All Python scripts now run correctly under both Python 2 and Python 3 #14
  • -
  • The "accept autosuggestion" key can now be configured #19
  • -
  • Autosuggestions will no longer suggest invalid commands #6
  • - -
- -

Release Notes for fishfish Beta r2

- -

Bug Fixes

-
    -
  • Implicit cd is back, for paths that start with one or two dots, a slash, or a tilde.
  • -
  • Overrides of default functions should be fixed. The "internalized scripts" feature is disabled for now.
  • -
  • Disabled delayed suspend. This is a strange job-control feature of BSD systems, including OS X. Disabling it frees up Control Y for other purposes; in particular, for yank, which now works on OS X.
  • -
  • fish_indent is fixed. In particular, the funced and funcsave functions work again. -
  • A SIGTERM now ends the whole execution stack again (resolving this issue). -
  • Bumped the __fish_config_interactive version number so the default fish_color_autosuggestion kicks in. -
  • fish_config better handles combined term256 and classic colors like "555 yellow". - -
- -

New Features

-
    -
  • A history builtin, and associated interactive function that enables deleting history items. Example usage: - -
      -
    • Print all history items beginning with echo: history --prefix echo
    • -
    • Print all history items containing foo: history --contains foo
    • -
    • Interactively delete some items containing foo: history --delete --contains foo
    • -

    -Credit to Siteshwar for implementation. Thanks Siteshwar! -
  • -
- -
- -

Release Notes for fishfish Beta r1

- -

Scripting

-
    -
  • No changes! All existing fish scripts, config files, completions, etc. from trunk should continue to work.
  • -
- -

New Features

-
    - -
  • Autosuggestions. Think URL fields in browsers. When you type a command, fish will suggest the rest of the command after the cursor, in a muted gray when possible. You can accept the suggestion with the right arrow key or Ctrl-F. Suggestions come from command history, completions, and some custom code for cd; there's a lot of potential for improvement here. The suggestions are computed on a background pthread, so they never slow down your typing. - -The autosuggestion feature is incredible. I miss it dearly every time I use anything else.
  • - -
  • term256 support where available, specifically modern xterms and OS X Lion. You can specify colors the old way ('set_color cyan') or by specifying RGB hex values ('set_color FF3333'); fish will pick the closest supported color. Some xterms do not advertise term256 support either in the $TERM or terminfo max_colors field, but nevertheless support it. For that reason, fish will default into using it on any xterm (but it can be disabled with an environment variable).
  • - -
  • Web-based configuration page. There is a new function 'fish_config'. This spins up a simple Python web server and opens a browser window to it. From this web page, you can set your shell colors and view your functions, variables, and history; all changes apply immediately to all running shells. Eventually all configuration ought to be supported via this mechanism (but in addition to, not instead of, command line mechanisms).
  • - -
  • Man page completions. There is a new function 'fish_update_completions'. This function reads all the man1 files from your manpath, removes the roff formatting, parses them to find the commands and options, and outputs fish completions into ~/.config/fish/completions. It won't overwrite existing completion files (except ones that it generated itself).
  • - -
- -

Programmatic Changes

- -
    - -
  • fish is now entirely in C++. I have no particular love for C++, but it provides a ready memory-model to replace halloc. We've made an effort to keep it to a sane and portable subset (no C++11, no boost, no going crazy with templates or smart pointers), but we do use the STL and a little tr1.
  • - -
  • halloc is entirely gone, replaced by normal C++ ownership semantics. If you don't know what halloc is, well, now you have two reasons to be happy.
  • - -
  • All the crufty C data structures are entirely gone. array_list_t, priority_queue_t, hash_table_t, string_buffer_t have been removed and replaced by STL equivalents like std::vector, std::map, and std::wstring. A lot of the string handling now uses std::wstring instead of wchar_t *
  • - -
  • fish now spawns pthreads for tasks like syntax highlighting that require blocking I/O.
  • - -
  • History has been completely rewritten. History files now use an extensible YAML-style syntax. History "merging" (multiple shells writing to the same history file) now works better. There is now a maximum history length of about 250k items (256 * 1024).
  • - -
  • The parser has been "instanced," so you can now create more than one.
  • - -
  • Total #LoC has shrunk slightly even with the new features.
  • -
- -

Performance

- -
    - -
  • fish now runs syntax highlighting in a background thread, so typing commands is always responsive even on slow filesystems.
  • - -
  • echo, test, and pwd are now builtins, which eliminates many forks.
  • - -
  • The files in share/functions and share/completions now get 'internalized' into C strings that get compiled in with fish. This substantially reduces the number of files touched at startup. A consequence is that you cannot change these functions without recompiling, but often other functions depend on these "standard" functions, so changing them is perhaps not a good idea anyways.
  • -
- -

Here are some system call counts for launching and then exiting fish with the default configuration, on OS X. The first column is fish trunk, the next column is with our changes, and the last column is bash for comparison. This data was collected via dtrace. - -

-           before   after    bash    
-  open          9       4       5
-  fork         28      14       0
-  stat        131      85      11
- lstat        670       0       0
-  read        332      80       4
- write        172     149       0
-
- -

The large number of forks relative to bash are due to fish's insanely expensive default prompt, which is unchanged in my version. If we switch to a prompt comparable to bash's (lame) default, the forks drop to 16 with trunk, 4 after our changes. - -

The large reduction in lstat() numbers is due to fish no longer needing to call ttyname() on OS X. - -

We've got some work to do to be as lean as bash, but we're on the right track. - - From 8eb9dac4bc83fe02b3833e51de3d1d430a6a61c8 Mon Sep 17 00:00:00 2001 From: David Adam Date: Fri, 8 Apr 2016 12:44:36 +0800 Subject: [PATCH 080/363] CHANGELOG: include release notes for previous releases [ci skip] --- CHANGELOG.md | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e42bd92e3..aa0b82941 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,157 @@ +# fish 2.2.0 (released July 12, 2015) + +### Significant changes ### + + * Abbreviations: the new `abbr` command allows for interactively-expanded abbreviations, allowing quick access to frequently-used commands (#731). + * Vi mode: run `fish_vi_mode` to switch fish into the key bindings and prompt familiar to users of the Vi editor (#65). + * New inline and interactive pager, which will be familiar to users of zsh (#291). + * Underlying architectural changes: the `fishd` universal variable server has been removed as it was a source of many bugs and security problems. Notably, old fish sessions will not be able to communicate universal variable changes with new fish sessions. For best results, restart all running instances of `fish`. + * The web-based configuration tool has been redesigned, featuring a prompt theme chooser and other improvements. + * New German, Brazilian Portuguese, and Chinese translations. + +### Backward-incompatible changes ### + +These are kept to a minimum, but either change undocumented features or are too hard to use in their existing forms. These changes may break existing scripts. + + * `commandline` no longer interprets functions "in reverse", instead behaving as expected (#1567). + * The previously-undocumented `CMD_DURATION` variable is now set for all commands and contains the execution time of the last command in milliseconds (#1585). It is no longer exported to other commands (#1896). + * `if` / `else` conditional statements now return values consistent with the Single Unix Specification, like other shells (#1443). + * A new "top-level" local scope has been added, allowing local variables declared on the commandline to be visible to subsequent commands. (#1908) + +### Other notable fixes and improvements ### + + * New documentation design (#1662), which requires a Doxygen version 1.8.7 or newer to build. + * Fish now defines a default directory for other packages to provide completions. By default this is `/usr/share/fish/vendor-completions.d`; on systems with `pkgconfig` installed this path is discoverable with `pkg-config --variable completionsdir fish`. + * A new parser removes many bugs; all existing syntax should keep working. + * New `fish_preexec` and `fish_postexec` events are fired before and after job execution respectively (#1549). + * Unmatched wildcards no longer prevent a job from running. Wildcards used interactively will still print an error, but the job will proceed and the wildcard will expand to zero arguments (#1482). + * The `.` command is deprecated and the `source` command is preferred (#310). + * `bind` supports "bind modes", which allows bindings to be set for a particular named mode, to support the implementation of Vi mode. + * A new `export` alias, which behaves like other shells (#1833). + * `command` has a new `--search` option to print the name of the disk file that would be executed, like other shells' `command -v` (#1540). + * `commandline` has a new `--paging-mode` option to support the new pager. + * `complete` has a new `--wraps` option, which allows a command to (recursively) inherit the completions of a wrapped command (#393), and `complete -e` now correctly erases completions (#380). + * Completions are now generated from manual pages by default on the first run of fish (#997). + * `fish_indent` can now produce colorized (`--ansi`) and HTML (`--html`) output (#1827). + * `functions --erase` now prevents autoloaded functions from being reloaded in the current session. + * `history` has a new `--merge` option, to incorporate history from other sessions into the current session (#825). + * `jobs` returns 1 if there are no active jobs (#1484). + * `read` has several new options: + * `--array` to break input into an array (#1540) + * `--null` to break lines on NUL characters rather than newlines (#1694) + * `--nchars` to read a specific number of characters (#1616) + * `--right-prompt` to display a right-hand-side prompt during interactive read (#1698). + * `type` has a new `-q` option to suppress output (#1540 and, like other shells, `type -a` now prints all matches for a command (#261). + * Pressing F1 now shows the manual page for the current command (#1063). + * `fish_title` functions have access to the arguments of the currently running argument as `$argv[1]` (#1542). + * The OS command-not-found handler is used on Arch Linux (#1925), nixOS (#1852), openSUSE and Fedora (#1280). + * `Alt`+`.` searches backwards in the token history, mapping to the same behavior as inserting the last argument of the previous command, like other shells (#89). + * The `SHLVL` environment variable is incremented correctly (#1634 & #1693). + * Added completions for `adb` (#1165 & #1211), `apt` (#2018), `aura` (#1292), `composer` (#1607), `cygport` (#1841), `dropbox` (#1533), `elixir` (#1167), `fossil`, `heroku` (#1790), `iex` (#1167), `kitchen` (#2000), `nix` (#1167), `node`/`npm` (#1566), `opam` (#1615), `setfacl` (#1752), `tmuxinator` (#1863), and `yast2` (#1739). + * Improved completions for `brew` (#1090 & #1810), `bundler` (#1779), `cd` (#1135), `emerge` (#1840),`git` (#1680, #1834 & #1951), `man` (#960), `modprobe` (#1124), `pacman` (#1292), `rpm` (#1236), `rsync` (#1872), `scp` (#1145), `ssh` (#1234), `sshfs` (#1268), `systemctl` (#1462, #1950 & #1972), `tmux` (#1853), `vagrant` (#1748), `yum` (#1269), and `zypper` (#1787). + +--- + +# fish 2.1.2 (released Feb 24, 2015) + +fish 2.1.2 contains a workaround for a filesystem bug in Mac OS X Yosemite. #1859 + +Specifically, after installing fish 2.1.1 and then rebooting, "Verify Disk" in Disk Utility will report "Invalid number of hard links." We don't have any reports of data loss or other adverse consequences. fish 2.1.2 avoids triggering the bug, but does not repair an already affected filesystem. To repair the filesystem, you can boot into Recovery Mode and use Repair Disk from Disk Utility. Linux and versions of OS X prior to Yosemite are believed to be unaffected. + +There are no other changes in this release. + +--- + +# fish 2.1.1 (released September 26, 2014) + +__Important:__ if you are upgrading, stop all running instances of `fishd` as soon as possible after installing this release; it will be restarted automatically. On most systems, there will be no further action required. Note that some environments (where `XDG_RUNTIME_DIR` is set), such as Fedora 20, will require a restart of all running fish processes before universal variables work as intended. + +Distributors are highly encouraged to call `killall fishd`, `pkill fishd` or similar in installation scripts, or to warn their users to do so. + +### Security fixes + * The fish_config web interface now uses an authentication token to protect requests and only responds to requests from the local machine with this token, preventing a remote code execution attack. (closing CVE-2014-2914). #1438 + * `psub` and `funced` are no longer vulnerable to attacks which allow local privilege escalation and data tampering (closing CVE-2014-2906 and CVE-2014-3856). #1437 + * `fishd` uses a secure path for its socket, preventing a local privilege escalation attack (closing CVE-2014-2905). #1436 + * `__fish_print_packages` is no longer vulnerable to attacks which would allow local privilege escalation and data tampering (closing CVE-2014-3219). #1440 + +### Other fixes + * `fishd` now ignores SIGPIPE, fixing crashes using tools like GNU Parallel and which occurred more often as a result of the other `fishd` changes. #1084 & #1690 + +--- + +# fish 2.1.0 + +Significant Changes +------------------- + +* **Tab completions will fuzzy-match files.** #568 + + When tab-completing a file, fish will first attempt prefix matches (`foo` matches `foobar`), then substring matches (`ooba` matches `foobar`), and lastly subsequence matches (`fbr` matches `foobar`). For example, in a directory with files foo1.txt, foo2.txt, foo3.txt…, you can type only the numeric part and hit tab to fill in the rest. + + This feature is implemented for files and executables. It is not yet implemented for options (like `--foobar`), and not yet implemented across path components (like `/u/l/b` to match `/usr/local/bin`). + +* **Redirections now work better across pipelines.** #110, #877 + + In particular, you can pipe stderr and stdout together, for example, with `cmd ^&1 | tee log.txt`, or the more familiar `cmd 2>&1 | tee log.txt`. + +* **A single `%` now expands to the last job backgrounded.** #1008 + + Previously, a single `%` would pid-expand to either all backgrounded jobs, or all jobs owned by your user. Now it expands to the last job backgrounded. If no job is in the background, it will fail to expand. In particular, `fg %` can be used to put the most recent background job in the foreground. + +Other Notable Fixes +------------------- + +* alt-U and alt+C now uppercase and capitalize words, respectively. #995 + +* VTE based terminals should now know the working directory. #906 + +* The autotools build now works on Mavericks. #968 + +* The end-of-line binding (ctrl+E) now accepts autosuggestions. #932 + +* Directories in `/etc/paths` (used on OS X) are now prepended instead of appended, similar to other shells. #927 + +* Option-right-arrow (used for partial autosuggestion completion) now works on iTerm2. #920 + +* Tab completions now work properly within nested subcommands. #913 + +* `printf` supports \e, the escape character. #910 + +* `fish_config history` no longer shows duplicate items. #900 + +* `$fish_user_paths` is now prepended to $PATH instead of appended. #888 + +* Jobs complete when all processes complete. #876 + + + For example, in previous versions of fish, `sleep 10 | echo Done` returns control immediately, because echo does not read from stdin. Now it does not complete until sleep exits (presumably after 10 seconds). + +* Better error reporting for square brackets. #875 + +* fish no longer tries to add `/bin` to `$PATH` unless PATH is totally empty. #852 + +* History token substitution (alt-up) now works correctly inside subshells. #833 + +* Flow control is now disabled, freeing up ctrl-S and ctrl-Q for other uses. #814 + +* sh-style variable setting like `foo=bar` now produces better error messages. #809 + +* Commands with wildcards no longer produce autosuggestions. #785 + +* funced no longer freaks out when supplied with no arguments. #780 + +* fish.app now works correctly in a directory containing spaces. #774 + +* Tab completion cycling no longer occasionally fails to repaint. #765 + +* Comments now work in eval'd strings. #684 + +* History search (up-arrow) now shows the item matching the autosuggestion, if that autosuggestion was truncated. #650 + +* Ctrl-T now transposes characters, as in other shells. #128 + +--- + # fish 2.0.0 Significant Changes From d7c690b4162ddd25c4ef6177a9e88db283caf9fa Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 8 Apr 2016 15:03:49 +0200 Subject: [PATCH 081/363] Fix "." and ".." paths in cd completions Previously if a directory called "a" was in $CDPATH, `cd ./a` would complete from there even if it was invalid. --- share/functions/__fish_complete_cd.fish | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/share/functions/__fish_complete_cd.fish b/share/functions/__fish_complete_cd.fish index cdbd02a78..3ba2ad9d5 100644 --- a/share/functions/__fish_complete_cd.fish +++ b/share/functions/__fish_complete_cd.fish @@ -1,7 +1,7 @@ function __fish_complete_cd -d "Completions for the cd command" set -l token (commandline -ct) - # Absolute path - no descriptions and no CDPATH - if string match -q '/*' -- $token + # Absolute path or explicitly from the current directory - no descriptions and no CDPATH + if string match -qr '^\.?\.?/.*' -- $token for d in $token*/ # Check if it's accessible - the glob only matches directories [ -x $d ]; and printf "%s\n" $d From 6c5f923a47640d535deef7247afb533a854454e2 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 8 Apr 2016 16:33:46 +0200 Subject: [PATCH 082/363] Remove non-existing functions in bind docs At least delete-line was previously a thing, but none of these are still available. First part of #2914 --- doc_src/bind.txt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/doc_src/bind.txt b/doc_src/bind.txt index 9b1f34a09..f645e32d8 100644 --- a/doc_src/bind.txt +++ b/doc_src/bind.txt @@ -85,20 +85,14 @@ The following special input functions are available: - `delete-char`, delete one character to the right of the cursor -- `delete-line`, delete the entire line - - `downcase-word`, make the current word lowercase -- `dump-functions`, print a list of all key-bindings - - `end-of-history`, move to the end of the history - `end-of-line`, move to the end of the line - `end-selection`, end selecting text -- `explain`, print a description of possible problems with the current command - - `forward-bigword`, move one whitespace-delimited word to the right - `forward-char`, move one character to the right From 6adc35c63622888ddfa876c488ca41910f8efe13 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Fri, 8 Apr 2016 15:52:10 -0700 Subject: [PATCH 083/363] add missing special-case for ../ When I reviewed the fix for #952 I noted that "../" wasn't handled but in my haste to merge it forgot to augment the pull-request. --- src/expand.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/expand.cpp b/src/expand.cpp index cb68eb980..72a72e77f 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -1818,8 +1818,9 @@ static expand_error_t expand_stage_wildcards(const wcstring &input, std::vector< In either case, we ignore the path if we start with ./ or /. Also ignore it if we are doing command completion and we contain a slash, per IEEE 1003.1, chapter 8 under PATH */ - if (string_prefixes_string(L"./", path_to_expand) || - string_prefixes_string(L"/", path_to_expand) || + if (string_prefixes_string(L"/", path_to_expand) || + string_prefixes_string(L"./", path_to_expand) || + string_prefixes_string(L"../", path_to_expand) || (for_command && path_to_expand.find(L'/') != wcstring::npos)) { effective_working_dirs.push_back(working_dir); From 2d5eaed745892a5e035caf7ccc02d27044f344fb Mon Sep 17 00:00:00 2001 From: Jak Wings Date: Fri, 8 Apr 2016 18:20:21 +0800 Subject: [PATCH 084/363] fix handling of line continuation in keywords This behavior is more consistent with line continuation in strings other than keywords. Fixes #2897 --- src/parse_tree.cpp | 11 ++++++++--- tests/line-continuation.err | 0 tests/line-continuation.in | 25 +++++++++++++++++++++++++ tests/line-continuation.out | 5 +++++ tests/line-continuation.status | 1 + 5 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 tests/line-continuation.err create mode 100644 tests/line-continuation.in create mode 100644 tests/line-continuation.out create mode 100644 tests/line-continuation.status diff --git a/src/parse_tree.cpp b/src/parse_tree.cpp index 3d2f7b8c7..cdaec9ef0 100644 --- a/src/parse_tree.cpp +++ b/src/parse_tree.cpp @@ -1254,6 +1254,12 @@ static parse_keyword_t keyword_with_name(const wchar_t *name) return result; } +static bool is_keyword_char(wchar_t c) +{ + return (c >= L'a' && c <= L'z') || (c >= L'A' && c <= L'Z') || (c >= L'0' && c <= L'9') + || c == L'\'' || c == L'"' || c == L'\\' || c == '\n'; +} + /* Given a token, returns the keyword it matches, or parse_keyword_none. */ static parse_keyword_t keyword_for_token(token_type tok, const wcstring &token) { @@ -1267,17 +1273,16 @@ static parse_keyword_t keyword_for_token(token_type tok, const wcstring &token) parse_keyword_t result = parse_keyword_none; bool needs_expand = false, all_chars_valid = true; const wchar_t *tok_txt = token.c_str(); - const wchar_t *chars_allowed_in_keywords = L"abcdefghijklmnopqrstuvwxyz'\""; for (size_t i=0; tok_txt[i] != L'\0'; i++) { wchar_t c = tok_txt[i]; - if (! wcschr(chars_allowed_in_keywords, c)) + if (! is_keyword_char(c)) { all_chars_valid = false; break; } // If we encounter a quote, we need expansion - needs_expand = needs_expand || c == L'"' || c == L'\''; + needs_expand = needs_expand || c == L'"' || c == L'\'' || c == L'\\'; } if (all_chars_valid) diff --git a/tests/line-continuation.err b/tests/line-continuation.err new file mode 100644 index 000000000..e69de29bb diff --git a/tests/line-continuation.in b/tests/line-continuation.in new file mode 100644 index 000000000..a1ac92932 --- /dev/null +++ b/tests/line-continuation.in @@ -0,0 +1,25 @@ +ech\ +o echo + +buil\ +tin echo builtin echo + +true; an\ +d echo true + +\i\ +\U00000066\ + true + echo if true +\ +\145n\ +d\ +; + +'if'\ + true + echo if true +"\ +en\ +d\ +"; diff --git a/tests/line-continuation.out b/tests/line-continuation.out new file mode 100644 index 000000000..053209a56 --- /dev/null +++ b/tests/line-continuation.out @@ -0,0 +1,5 @@ +echo +builtin echo +true +if true +if true diff --git a/tests/line-continuation.status b/tests/line-continuation.status new file mode 100644 index 000000000..573541ac9 --- /dev/null +++ b/tests/line-continuation.status @@ -0,0 +1 @@ +0 From 574851f092358f5834afb0b529676924fcbd59c6 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Fri, 8 Apr 2016 19:09:01 -0700 Subject: [PATCH 085/363] Fix #2919 by removing the entire block. This code represents only risk and does nothing useful for anything that can compile fish. In C++ situations where __STDC_VERSION__ is unset (as it should be), fish was assuming we are on < C99 and setting it to __FUNCTION__. Basically always, __PRETTY_FUNCTION__ ends up reaplaced by __FUNCTION__, this hurt error message usefulness and richness. __PRETTY_FUNCTION__: const thing::sub(int) __FUNCTION__: sub --- src/fallback.h | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/fallback.h b/src/fallback.h index f3252c149..0623ec54a 100644 --- a/src/fallback.h +++ b/src/fallback.h @@ -23,19 +23,6 @@ int fish_wcswidth(const wchar_t *str, size_t n); #define WCHAR_MAX INT_MAX #endif -/** - Make sure __func__ is defined to some string. In C99, this should - be the currently compiled function. If we aren't using C99 or - later, older versions of GCC had __FUNCTION__. -*/ -#if __STDC_VERSION__ < 199901L -# if __GNUC__ >= 2 -# define __func__ __FUNCTION__ -# else -# define __func__ "" -# endif -#endif - /** Under curses, tputs expects an int (*func)(char) as its last parameter, but in ncurses, tputs expects a int (*func)(int) as its From 7ad6a90ea2e75388d42b5b223d9273119994037e Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 10 Apr 2016 01:11:09 -0700 Subject: [PATCH 086/363] Change parser_token_types from wcstring to const wchar_t * Reduces allocations and startup time --- src/fish_indent.cpp | 2 +- src/parse_constants.h | 2 +- src/parse_tree.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fish_indent.cpp b/src/fish_indent.cpp index 0e31f28e7..d2e239438 100644 --- a/src/fish_indent.cpp +++ b/src/fish_indent.cpp @@ -104,7 +104,7 @@ static void dump_node(indent_t node_indent, const parse_node_t &node, const wcst } fwprintf(stderr, L"{off %4d, len %4d, indent %2u, %ls} [%ls|%ls|%ls]\n", node.source_start, node.source_length, node_indent, - parser_token_types[node.type].c_str(), prevc_str, source.substr(node.source_start, + parser_token_types[node.type], prevc_str, source.substr(node.source_start, node.source_length).c_str(), nextc_str); } diff --git a/src/parse_constants.h b/src/parse_constants.h index afc88c6c2..03dffd285 100644 --- a/src/parse_constants.h +++ b/src/parse_constants.h @@ -83,7 +83,7 @@ enum parse_token_type_t LAST_PARSE_TOKEN_TYPE = parse_token_type_end } __packed; // Array of strings corresponding to the enums above instantiated in parse_tree.cpp. -extern wcstring parser_token_types[]; +extern const wchar_t * const parser_token_types[]; /* These must be maintained in sorted order (except for none, which isn't a keyword). This enables us to do binary search. */ enum parse_keyword_t diff --git a/src/parse_tree.cpp b/src/parse_tree.cpp index cdaec9ef0..ca6e310b9 100644 --- a/src/parse_tree.cpp +++ b/src/parse_tree.cpp @@ -17,7 +17,7 @@ #include // This array provides strings for each symbol in enum parse_token_type_t in parse_constants.h. -wcstring parser_token_types[] = { +const wchar_t * const parser_token_types[] = { L"token_type_invalid", L"symbol_job_list", L"symbol_job", From 59f0261dba993a5d632ce6e9963270a582d162e6 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sat, 9 Apr 2016 18:56:13 -0700 Subject: [PATCH 087/363] enhance fish_indent to normalize keywords Fish keywords can be quoted and split across lines. Prior to this change `fish_indent` would retain such odd, obfuscated, formatting. This change results in all keywords being converted to their canonical form. This required fixing a bug: the keyword member of parse_node_t wasn't being populated. This hadn't been noticed prior to now because it wasn't used. Fixes #2921 --- src/fish_indent.cpp | 14 ++++++++++---- src/parse_constants.h | 7 ++++++- src/parse_tree.cpp | 6 ++++-- tests/indent.in | 15 +++++++++++++++ tests/indent.out | 10 ++++++++++ 5 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/fish_indent.cpp b/src/fish_indent.cpp index d2e239438..39d296e54 100644 --- a/src/fish_indent.cpp +++ b/src/fish_indent.cpp @@ -102,10 +102,10 @@ static void dump_node(indent_t node_indent, const parse_node_t &node, const wcst nextc_str[1] = L'c'; nextc_str[2] = nextc + '@'; } - fwprintf(stderr, L"{off %4d, len %4d, indent %2u, %ls} [%ls|%ls|%ls]\n", + fwprintf(stderr, L"{off %4d, len %4d, indent %2u, kw %ls, %ls} [%ls|%ls|%ls]\n", node.source_start, node.source_length, node_indent, - parser_token_types[node.type], prevc_str, source.substr(node.source_start, - node.source_length).c_str(), nextc_str); + keyword_description(node.keyword).c_str(), parser_token_types[node.type], + prevc_str, source.substr(node.source_start, node.source_length).c_str(), nextc_str); } static void prettify_node_recursive(const wcstring &source, const parse_node_tree_t &tree, @@ -153,7 +153,13 @@ static void prettify_node_recursive(const wcstring &source, const parse_node_tre else if ((node_type >= FIRST_PARSE_TOKEN_TYPE && node_type <= LAST_PARSE_TOKEN_TYPE) || node_type == parse_special_type_parse_error) { - if (node.has_source()) + if (node.keyword != parse_keyword_none) + { + append_whitespace(node_indent, do_indent, *has_new_line, out_result); + out_result->append(keyword_description(node.keyword)); + *has_new_line = false; + } + else if (node.has_source()) { // Some type representing a particular token. if (prev_node_type != parse_token_type_redirection) diff --git a/src/parse_constants.h b/src/parse_constants.h index 03dffd285..4668516b9 100644 --- a/src/parse_constants.h +++ b/src/parse_constants.h @@ -73,6 +73,7 @@ enum parse_token_type_t parse_special_type_parse_error, parse_special_type_tokenizer_error, parse_special_type_comment, + LAST_TOKEN_TYPE = parse_special_type_comment, FIRST_TERMINAL_TYPE = parse_token_type_string, LAST_TERMINAL_TYPE = parse_token_type_terminate, @@ -85,7 +86,11 @@ enum parse_token_type_t // Array of strings corresponding to the enums above instantiated in parse_tree.cpp. extern const wchar_t * const parser_token_types[]; -/* These must be maintained in sorted order (except for none, which isn't a keyword). This enables us to do binary search. */ +// These must be maintained in sorted order (except for none, which isn't a keyword). This enables +// us to do binary search. +// +// IMPORTANT: If the following enum is modified you must update the corresponding keyword_map array +// in parse_tree.cpp. enum parse_keyword_t { parse_keyword_none, diff --git a/src/parse_tree.cpp b/src/parse_tree.cpp index ca6e310b9..adb2984fa 100644 --- a/src/parse_tree.cpp +++ b/src/parse_tree.cpp @@ -1078,9 +1078,11 @@ bool parse_ll_t::top_node_handle_terminal_types(parse_token_t token) if (matched) { - // Success. Tell the node that it matched this token, and what its source range is - // In the parse phase, we only set source ranges for terminal types. We propagate ranges to parent nodes afterwards. + // Success. Tell the node that it matched this token, and what its source range is in + // the parse phase, we only set source ranges for terminal types. We propagate ranges to + // parent nodes afterwards. parse_node_t &node = node_for_top_symbol(); + node.keyword = token.keyword; node.source_start = token.source_start; node.source_length = token.source_length; } diff --git a/tests/indent.in b/tests/indent.in index a1143c676..2d98dd8eb 100644 --- a/tests/indent.in +++ b/tests/indent.in @@ -89,3 +89,18 @@ echo \nTest redir formatting echo -n ' echo < stdin >>appended yes 2>&1 no > stdout maybe 2>& 4 | cat 2>| cat ' | ../test/root/bin/fish_indent + +echo \nTest normalization of keywords +# issue 2921 +echo -n ' +i\ +f true + echo yes +en\ +d + +"whil\ +e" true + "builtin" yes +en"d" +' | ../test/root/bin/fish_indent diff --git a/tests/indent.out b/tests/indent.out index 6b38a72fd..5a4d9dc0f 100644 --- a/tests/indent.out +++ b/tests/indent.out @@ -95,3 +95,13 @@ end Test redir formatting echo >appended yes 2>&1 no >stdout maybe 2>&4 | cat 2>| cat + +Test normalization of keywords + +if true + echo yes +end + +while true + builtin yes +end From 46840ae3753bb48fd6042b6e140ae1271dc84fdc Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sun, 10 Apr 2016 19:08:07 -0700 Subject: [PATCH 088/363] another wcstring to wchar_t * change In keeping with the change made by @ridiculousfish earlier today modify the `keyword_description()` function to return a const wchar_t pointer. Also, simplify the `token_type_description()` function to use the recently introduced mapping array. This changes the wording of many of the token type descriptions. However, I can't see this as being a problem since the original descriptions (e.g., "token_redirection") are no clearer to someone not acquainted with the implementation. --- src/fish_indent.cpp | 2 +- src/fish_tests.cpp | 4 +- src/parse_productions.cpp | 8 +-- src/parse_tree.cpp | 131 +++++++------------------------------- src/parse_tree.h | 4 +- 5 files changed, 32 insertions(+), 117 deletions(-) diff --git a/src/fish_indent.cpp b/src/fish_indent.cpp index 39d296e54..a469ccf76 100644 --- a/src/fish_indent.cpp +++ b/src/fish_indent.cpp @@ -104,7 +104,7 @@ static void dump_node(indent_t node_indent, const parse_node_t &node, const wcst } fwprintf(stderr, L"{off %4d, len %4d, indent %2u, kw %ls, %ls} [%ls|%ls|%ls]\n", node.source_start, node.source_length, node_indent, - keyword_description(node.keyword).c_str(), parser_token_types[node.type], + keyword_description(node.keyword), token_type_description(node.type), prevc_str, source.substr(node.source_start, node.source_length).c_str(), nextc_str); } diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 6250513cb..ef1956cca 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -3628,11 +3628,11 @@ static void test_new_parser_ll2(void) const parse_node_tree_t::parse_node_list_t node_list = tree.find_nodes(tree.at(0), tests2[i].type); if (node_list.size() == 0) { - err(L"Failed to find node of type '%ls'", token_type_description(tests2[i].type).c_str()); + err(L"Failed to find node of type '%ls'", token_type_description(tests2[i].type)); } else if (node_list.size() > 1) { - err(L"Found too many nodes of type '%ls'", token_type_description(tests2[i].type).c_str()); + err(L"Found too many nodes of type '%ls'", token_type_description(tests2[i].type)); } } } diff --git a/src/parse_productions.cpp b/src/parse_productions.cpp index 1a44ff487..6ec8e56a2 100644 --- a/src/parse_productions.cpp +++ b/src/parse_productions.cpp @@ -409,7 +409,7 @@ const production_t *parse_productions::production_for_token(parse_token_type_t n const bool log_it = false; if (log_it) { - fprintf(stderr, "Resolving production for %ls with input token <%ls>\n", token_type_description(node_type).c_str(), input1.describe().c_str()); + fprintf(stderr, "Resolving production for %ls with input token <%ls>\n", token_type_description(node_type), input1.describe().c_str()); } /* Fetch the function to resolve the list of productions */ @@ -452,14 +452,14 @@ const production_t *parse_productions::production_for_token(parse_token_type_t n case parse_token_type_background: case parse_token_type_end: case parse_token_type_terminate: - fprintf(stderr, "Terminal token type %ls passed to %s\n", token_type_description(node_type).c_str(), __FUNCTION__); + fprintf(stderr, "Terminal token type %ls passed to %s\n", token_type_description(node_type), __FUNCTION__); PARSER_DIE(); break; case parse_special_type_parse_error: case parse_special_type_tokenizer_error: case parse_special_type_comment: - fprintf(stderr, "Special type %ls passed to %s\n", token_type_description(node_type).c_str(), __FUNCTION__); + fprintf(stderr, "Special type %ls passed to %s\n", token_type_description(node_type), __FUNCTION__); PARSER_DIE(); break; @@ -477,7 +477,7 @@ const production_t *parse_productions::production_for_token(parse_token_type_t n { if (log_it) { - fprintf(stderr, "Node type '%ls' has no production for input '%ls' (in %s)\n", token_type_description(node_type).c_str(), input1.describe().c_str(), __FUNCTION__); + fprintf(stderr, "Node type '%ls' has no production for input '%ls' (in %s)\n", token_type_description(node_type), input1.describe().c_str(), __FUNCTION__); } } diff --git a/src/parse_tree.cpp b/src/parse_tree.cpp index adb2984fa..95493955d 100644 --- a/src/parse_tree.cpp +++ b/src/parse_tree.cpp @@ -17,7 +17,7 @@ #include // This array provides strings for each symbol in enum parse_token_type_t in parse_constants.h. -const wchar_t * const parser_token_types[] = { +const wchar_t * const token_type_map[] = { L"token_type_invalid", L"symbol_job_list", L"symbol_job", @@ -165,101 +165,16 @@ void parse_error_offset_source_start(parse_error_list_t *errors, size_t amt) } } -/** Returns a string description of the given token type */ -wcstring token_type_description(parse_token_type_t type) +// Returns a string description for the given token type. +const wchar_t *token_type_description(parse_token_type_t type) { - switch (type) - { - case token_type_invalid: - return L"invalid"; + if (type >= 0 && type <= LAST_TOKEN_TYPE) return token_type_map[type]; - case symbol_job_list: - return L"job_list"; - case symbol_job: - return L"job"; - case symbol_job_continuation: - return L"job_continuation"; - - case symbol_statement: - return L"statement"; - case symbol_block_statement: - return L"block_statement"; - case symbol_block_header: - return L"block_header"; - case symbol_for_header: - return L"for_header"; - case symbol_while_header: - return L"while_header"; - case symbol_begin_header: - return L"begin_header"; - case symbol_function_header: - return L"function_header"; - - case symbol_if_statement: - return L"if_statement"; - case symbol_if_clause: - return L"if_clause"; - case symbol_else_clause: - return L"else_clause"; - case symbol_else_continuation: - return L"else_continuation"; - - case symbol_switch_statement: - return L"switch_statement"; - case symbol_case_item_list: - return L"case_item_list"; - case symbol_case_item: - return L"case_item"; - - case symbol_andor_job_list: - return L"andor_job_list"; - case symbol_argument_list: - return L"argument_list"; - case symbol_freestanding_argument_list: - return L"freestanding_argument_list"; - - case symbol_boolean_statement: - return L"boolean_statement"; - case symbol_decorated_statement: - return L"decorated_statement"; - case symbol_plain_statement: - return L"plain_statement"; - case symbol_arguments_or_redirections_list: - return L"arguments_or_redirections_list"; - case symbol_argument_or_redirection: - return L"argument_or_redirection"; - case symbol_argument: - return L"symbol_argument"; - case symbol_redirection: - return L"symbol_redirection"; - case symbol_optional_background: - return L"optional_background"; - case symbol_end_command: - return L"symbol_end_command"; - - - case parse_token_type_string: - return L"token_string"; - case parse_token_type_pipe: - return L"token_pipe"; - case parse_token_type_redirection: - return L"token_redirection"; - case parse_token_type_background: - return L"token_background"; - case parse_token_type_end: - return L"token_end"; - case parse_token_type_terminate: - return L"token_terminate"; - - case parse_special_type_parse_error: - return L"parse_error"; - case parse_special_type_tokenizer_error: - return L"tokenizer_error"; - case parse_special_type_comment: - return L"comment"; - - } - return format_string(L"Unknown token type %ld", static_cast(type)); + // This leaks memory but it should never be run unless we have a bug elsewhere in the code. + const wcstring d = format_string(L"unknown_token_type_%ld", static_cast(type)); + wchar_t *d2 = new wchar_t[d.size() + 1]; + // cppcheck-suppress memleak + return std::wcscpy(d2, d.c_str()); } #define LONGIFY(x) L ## x @@ -291,23 +206,22 @@ keyword_map[] = KEYWORD_MAP(while) }; -wcstring keyword_description(parse_keyword_t k) +const wchar_t *keyword_description(parse_keyword_t type) { - if (k >= 0 && k <= LAST_KEYWORD) - { - return keyword_map[k].name; - } - else - { - return format_string(L"Unknown keyword type %ld", static_cast(k)); - } + if (type >= 0 && type <= LAST_KEYWORD) return keyword_map[type].name; + + // This leaks memory but it should never be run unless we have a bug elsewhere in the code. + const wcstring d = format_string(L"unknown_keyword_%ld", static_cast(type)); + wchar_t *d2 = new wchar_t[d.size() + 1]; + // cppcheck-suppress memleak + return std::wcscpy(d2, d.c_str()); } static wcstring token_type_user_presentable_description(parse_token_type_t type, parse_keyword_t keyword = parse_keyword_none) { if (keyword != parse_keyword_none) { - return format_string(L"keyword '%ls'", keyword_description(keyword).c_str()); + return format_string(L"keyword '%ls'", keyword_description(keyword)); } switch (type) @@ -338,7 +252,7 @@ static wcstring token_type_user_presentable_description(parse_token_type_t type, return L"end of the input"; default: - return format_string(L"a %ls", token_type_description(type).c_str()); + return format_string(L"a %ls", token_type_description(type)); } } @@ -383,7 +297,7 @@ wcstring parse_token_t::describe() const wcstring result = token_type_description(type); if (keyword != parse_keyword_none) { - append_format(result, L" <%ls>", keyword_description(keyword).c_str()); + append_format(result, L" <%ls>", keyword_description(keyword)); } return result; } @@ -542,7 +456,7 @@ struct parse_stack_element_t wcstring result = token_type_description(type); if (keyword != parse_keyword_none) { - append_format(result, L" <%ls>", keyword_description(keyword).c_str()); + append_format(result, L" <%ls>", keyword_description(keyword)); } return result; } @@ -611,7 +525,8 @@ class parse_ll_t { parse_token_type_t type = production_element_type(elem); parse_keyword_t keyword = production_element_keyword(elem); - fprintf(stderr, "\t%ls <%ls>\n", token_type_description(type).c_str(), keyword_description(keyword).c_str()); + fprintf(stderr, "\t%ls <%ls>\n", token_type_description(type), + keyword_description(keyword)); count++; } } diff --git a/src/parse_tree.h b/src/parse_tree.h index 67632685f..8ae9ee88d 100644 --- a/src/parse_tree.h +++ b/src/parse_tree.h @@ -63,8 +63,8 @@ typedef unsigned int parse_tree_flags_t; wcstring parse_dump_tree(const parse_node_tree_t &tree, const wcstring &src); -wcstring token_type_description(parse_token_type_t type); -wcstring keyword_description(parse_keyword_t type); +const wchar_t *token_type_description(parse_token_type_t type); +const wchar_t *keyword_description(parse_keyword_t type); /* Node flags */ enum From 0993141334a08697351fc7900473f46b0d092216 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Mon, 11 Apr 2016 16:47:46 -0700 Subject: [PATCH 089/363] retry flakey tests on failure Fixes #2926 --- tests/interactive.fish | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/tests/interactive.fish b/tests/interactive.fish index 9e0d344f5..76b0562e3 100644 --- a/tests/interactive.fish +++ b/tests/interactive.fish @@ -4,6 +4,9 @@ # should be running it via `make test` to ensure the environment is properly # setup. +# This is a list of flakey tests that often succeed when rerun. +set TESTS_TO_RETRY bind.expect + # Change to directory containing this script cd (dirname (status -f)) @@ -69,10 +72,19 @@ function test_file end end -set -l failed +set failed for i in $files_to_test if not test_file $i - set failed $failed $i + # Retry flakey tests once. + if contains $i $TESTS_TO_RETRY + say -o cyan "Rerunning test $i since it is known to be flakey" + rm -f $i.tmp.* + if not test_file $i + set failed $failed $i + end + else + set failed $failed $i + end end end From 85799ee86ee18915e79d2ae8cf3f23f504249d89 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 12 Apr 2016 21:40:14 +0200 Subject: [PATCH 090/363] Remove the default self-insert binding in vi-default mode Fixes #2832. --- share/functions/fish_vi_key_bindings.fish | 2 ++ 1 file changed, 2 insertions(+) diff --git a/share/functions/fish_vi_key_bindings.fish b/share/functions/fish_vi_key_bindings.fish index 75be6644b..f63956677 100644 --- a/share/functions/fish_vi_key_bindings.fish +++ b/share/functions/fish_vi_key_bindings.fish @@ -19,6 +19,8 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' fish_default_key_bindings -M insert fish_default_key_bindings -M default + # Remove the default self-insert bindings in default mode + bind -e "" -M default # Add way to kill current command line while in insert mode. bind -M insert \cc 'commandline ""' # Add a way to switch from insert to normal (command) mode. From 6431c0de16937622a2664c417f517d002220e017 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Tue, 12 Apr 2016 18:32:20 -0700 Subject: [PATCH 091/363] fix bug in lint.fish helper script I just noticed that depending on the state of your working tree there can be one or more spaces after the modification token and the file name. If there is more than one space that causes the `string split` to produce unexpected output. --- build_tools/lint.fish | 23 +++++++------------- src/fish.cpp | 49 +++++++++++++++++++------------------------ 2 files changed, 30 insertions(+), 42 deletions(-) diff --git a/build_tools/lint.fish b/build_tools/lint.fish index 7182322f0..9585d4e69 100755 --- a/build_tools/lint.fish +++ b/build_tools/lint.fish @@ -34,13 +34,8 @@ if test $all = yes else # We haven't been asked to lint all the source. If there are uncommitted # changes lint those, else lint the files in the most recent commit. - set pending (git status --porcelain --short --untracked-files=all | sed -e 's/^ *//') - if set -q pending[1] - # There are pending changes so lint those files. - for arg in $pending - set files $files (string split -m 1 ' ' $arg)[2] - end - else + set files (git status --porcelain --short --untracked-files=all | sed -e 's/^ *[^ ]* *//') + if not set -q files[1] # No pending changes so lint the files in the most recent commit. set files (git show --word-diff=porcelain --name-only --pretty=oneline head)[2..-1] end @@ -56,10 +51,9 @@ if set -q c_files[1] echo ======================================== echo Running cppcheck echo ======================================== - # The stderr to stdout redirection is because cppcheck, incorrectly - # IMHO, writes its diagnostic messages to stderr. Anyone running - # this who wants to capture its output will expect those messages to be - # written to stdout. + # The stderr to stdout redirection is because cppcheck, incorrectly IMHO, writes its + # diagnostic messages to stderr. Anyone running this who wants to capture its output will + # expect those messages to be written to stdout. cppcheck -q --verbose --std=posix --std=c11 --language=c++ --template "[{file}:{line}]: {severity} ({id}): {message}" --suppress=missingIncludeSystem --inline-suppr --enable=$cppchecks $cppcheck_args $c_files 2>& 1 end @@ -68,10 +62,9 @@ if set -q c_files[1] echo ======================================== echo Running oclint echo ======================================== - # The stderr to stdout redirection is because oclint, incorrectly - # writes its final summary counts of the errors detected to stderr. - # Anyone running this who wants to capture its output will expect those - # messages to be written to stdout. + # The stderr to stdout redirection is because oclint, incorrectly writes its final summary + # counts of the errors detected to stderr. Anyone running this who wants to capture its + # output will expect those messages to be written to stdout. if test (uname -s) = "Darwin" if not test -f compile_commands.json xcodebuild > xcodebuild.log diff --git a/src/fish.cpp b/src/fish.cpp index 17cd1f16a..bde7854ee 100644 --- a/src/fish.cpp +++ b/src/fish.cpp @@ -92,39 +92,34 @@ extern "C" { int _NSGetExecutablePath(char* buf, uint32_t* bufsize); } -/* Return the path to the current executable. This needs to be realpath'd. */ +// Return the path to the current executable. This needs to be realpath'd. static std::string get_executable_path(const char *argv0) { - char buff[PATH_MAX]; + char buff[PATH_MAX + 1]; + #if __APPLE__ - { - /* Returns 0 on success, -1 if the buffer is too small */ - 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); - return result; - } -#endif - { - /* 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 - { - return std::string(buff); + // On OS X use it's proprietary API to get the path to the executable. + uint32_t buffSize = sizeof buff; + if (_NSGetExecutablePath(buff, &buffSize) == 0) return std::string(buff); +#else + // On non-OS X UNIXes, try /proc directory. + ssize_t len; + len = readlink("/proc/self/exe", buff, sizeof buff); // Linux + if (len == -1) { + len = readlink("/proc/curproc/file", buff, sizeof buff); // BSD + if (len == -1) { + len = readlink("/proc/self/path/a.out", buff, sizeof buff); // Solaris } } + if (len > 0) { + buff[len] = '\0'; + return std::string(buff); + } +#endif - /* 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. */ + // 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 : ""); } From 9569f51e83b18fee9eb7d3494540530ad809555f Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Tue, 12 Apr 2016 19:01:28 -0700 Subject: [PATCH 092/363] revert inadvertent file inclusion in prior commit Commit 6431c0de16937622a2664c417f517d002220e017 was not meant to include changes to fish.cpp. --- src/fish.cpp | 49 +++++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/src/fish.cpp b/src/fish.cpp index bde7854ee..17cd1f16a 100644 --- a/src/fish.cpp +++ b/src/fish.cpp @@ -92,34 +92,39 @@ extern "C" { int _NSGetExecutablePath(char* buf, uint32_t* bufsize); } -// Return the path to the current executable. This needs to be realpath'd. +/* Return the path to the current executable. This needs to be realpath'd. */ static std::string get_executable_path(const char *argv0) { - char buff[PATH_MAX + 1]; - + char buff[PATH_MAX]; #if __APPLE__ - // On OS X use it's proprietary API to get the path to the executable. - uint32_t buffSize = sizeof buff; - if (_NSGetExecutablePath(buff, &buffSize) == 0) return std::string(buff); -#else - // On non-OS X UNIXes, try /proc directory. - ssize_t len; - len = readlink("/proc/self/exe", buff, sizeof buff); // Linux - if (len == -1) { - len = readlink("/proc/curproc/file", buff, sizeof buff); // BSD - if (len == -1) { - len = readlink("/proc/self/path/a.out", buff, sizeof buff); // Solaris - } - } - if (len > 0) { - buff[len] = '\0'; - return std::string(buff); + { + /* Returns 0 on success, -1 if the buffer is too small */ + 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); + return result; } #endif + { + /* 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 + { + 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. + /* 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 : ""); } From 8e103c231e74a3dda40d0a0e5a844ea8b6aa1e4b Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Tue, 12 Apr 2016 19:03:07 -0700 Subject: [PATCH 093/363] fish handling of readlink() The readlink() function does not null terminate the path it returns. Remove the OS X code that deals with a path buffer that is too short. For one thing a loop isn't needed since we're told how big of a buffer is required if the first _NSGetExecutablePath() call fails. But more important it is so unlikely that the path will be longer than PATH_MAX that if it is we should just give up. Fixes 2931. --- src/fish.cpp | 49 ++++++++++++++++++++++--------------------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/src/fish.cpp b/src/fish.cpp index 17cd1f16a..bde7854ee 100644 --- a/src/fish.cpp +++ b/src/fish.cpp @@ -92,39 +92,34 @@ extern "C" { int _NSGetExecutablePath(char* buf, uint32_t* bufsize); } -/* Return the path to the current executable. This needs to be realpath'd. */ +// Return the path to the current executable. This needs to be realpath'd. static std::string get_executable_path(const char *argv0) { - char buff[PATH_MAX]; + char buff[PATH_MAX + 1]; + #if __APPLE__ - { - /* Returns 0 on success, -1 if the buffer is too small */ - 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); - return result; - } -#endif - { - /* 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 - { - return std::string(buff); + // On OS X use it's proprietary API to get the path to the executable. + uint32_t buffSize = sizeof buff; + if (_NSGetExecutablePath(buff, &buffSize) == 0) return std::string(buff); +#else + // On non-OS X UNIXes, try /proc directory. + ssize_t len; + len = readlink("/proc/self/exe", buff, sizeof buff); // Linux + if (len == -1) { + len = readlink("/proc/curproc/file", buff, sizeof buff); // BSD + if (len == -1) { + len = readlink("/proc/self/path/a.out", buff, sizeof buff); // Solaris } } + if (len > 0) { + buff[len] = '\0'; + return std::string(buff); + } +#endif - /* 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. */ + // 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 : ""); } From 706bfa70c144775126e15a960028d3b3df7980fb Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Tue, 12 Apr 2016 19:57:07 -0700 Subject: [PATCH 094/363] improve the style.fish script If there are uncommitted changes use `git-clang-format` to limit the style fixups to the lines being modified. Refuse to do a `make style-all` if there are uncommitted changes. Include a fix for the parsing of `git status` output that was recently incorporated into the lint.fish script. --- CONTRIBUTING.md | 6 +++++- build_tools/style.fish | 42 +++++++++++++++++++++++++++++++----------- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2c8473190..e73bc3446 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -40,7 +40,9 @@ The following sections discuss the specific rules for the style that should be u make style ``` -before commiting your change. If you've already committed your changes that's okay since it will then check the files in the most recent commit. This can be useful after you've merged someone elses change and want to check that it's style is acceptable. +before commiting your change. That will run `git-clang-format` to rewrite just the lines you're modifying. + +If you've already committed your changes that's okay since it will then check the files in the most recent commit. This can be useful after you've merged someone elses change and want to check that it's style is acceptable. However, in that case it will run `clang-format` to ensure the entire file, not just the lines modified by the commit, conform to the style. If you want to check the style of the entire code base run @@ -48,6 +50,8 @@ If you want to check the style of the entire code base run make style-all ``` +That command will refuse to restyle any files if you have uncommitted changes. + ### Suppressing Reformatting of the Code If you have a good reason for doing so you can tell `clang-format` to not reformat a block of code by enclosing it in comments like this: diff --git a/build_tools/style.fish b/build_tools/style.fish index 76cf920d5..62d8d5c7e 100755 --- a/build_tools/style.fish +++ b/build_tools/style.fish @@ -6,6 +6,7 @@ # This runs C++ files and fish scripts (*.fish) through their respective code # formatting programs. # +set git_clang_format no set c_files set f_files set all no @@ -21,17 +22,21 @@ if set -q argv[1] end if test $all = yes + set files (git status --porcelain --short --untracked-files=all | sed -e 's/^ *[^ ]* *//') + if set -q files[1] + echo + echo You have uncommited changes. Cowardly refusing to restyle the entire code base. + echo + exit 1 + end set c_files src/*.h src/*.cpp set f_files ***.fish else - # We haven't been asked to reformat all the source. If there are uncommitted - # changes reformat those, else reformat the files in the most recent commit. - set pending (git status --porcelain --short --untracked-files=all | sed -e 's/^ *//') - if count $pending > /dev/null - # There are pending changes so lint those files. - for arg in $pending - set files $files (string split -m 1 ' ' $arg)[2] - end + # We haven't been asked to reformat all the source. If there are uncommitted changes reformat + # those using `git clang-format`. Else reformat the files in the most recent commit. + set files (git status --porcelain --short --untracked-files=all | sed -e 's/^ *[^ ]* *//') + if set -q files[1] + set git_clang_format yes else # No pending changes so lint the files in the most recent commit. set files (git show --name-only --pretty=oneline head | tail --lines=+2) @@ -45,18 +50,32 @@ end # Run the C++ reformatter if we have any C++ files. if set -q c_files[1] - if type -q clang-format + if test $git_clang_format = yes + if type -q git-clang-format + echo + echo ======================================== + echo Running git-clang-format + echo ======================================== + git add $c_files + git-clang-format + else + echo + echo 'WARNING: Cannot find git-clang-format command' + echo + end + else if type -q clang-format echo echo ======================================== echo Running clang-format echo ======================================== for file in $c_files - clang-format $file > $file.new + clang-format $file >$file.new if cmp --quiet $file $file.new echo $file was correctly formatted rm $file.new else echo $file was NOT correctly formatted + chmod --reference=$file $file.new mv $file.new $file end end @@ -78,12 +97,13 @@ if set -q f_files[1] echo Running fish_indent echo ======================================== for file in $f_files - fish_indent < $file > $file.new + fish_indent <$file >$file.new if cmp --quiet $file $file.new echo $file was correctly formatted rm $file.new else echo $file was NOT correctly formatted + chmod --reference=$file $file.new mv $file.new $file end end From ea3d9c36a52dcf6bcde0ff41af8ca19daca2267e Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Wed, 13 Apr 2016 09:00:07 -0700 Subject: [PATCH 095/363] fix off by one error --- src/fish.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/fish.cpp b/src/fish.cpp index bde7854ee..9676814de 100644 --- a/src/fish.cpp +++ b/src/fish.cpp @@ -62,9 +62,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA #include "input_common.h" #include "wildcard.h" -/* PATH_MAX may not exist */ +// PATH_MAX may not exist. #ifndef PATH_MAX -#define PATH_MAX 1024 +#define PATH_MAX 4096 #endif /* If we are doing profiling, the filename to output to */ @@ -95,7 +95,7 @@ extern "C" { // Return the path to the current executable. This needs to be realpath'd. static std::string get_executable_path(const char *argv0) { - char buff[PATH_MAX + 1]; + char buff[PATH_MAX]; #if __APPLE__ // On OS X use it's proprietary API to get the path to the executable. @@ -104,11 +104,11 @@ static std::string get_executable_path(const char *argv0) #else // On non-OS X UNIXes, try /proc directory. ssize_t len; - len = readlink("/proc/self/exe", buff, sizeof buff); // Linux + len = readlink("/proc/self/exe", buff, sizeof buff - 1); // Linux if (len == -1) { - len = readlink("/proc/curproc/file", buff, sizeof buff); // BSD + len = readlink("/proc/curproc/file", buff, sizeof buff - 1); // BSD if (len == -1) { - len = readlink("/proc/self/path/a.out", buff, sizeof buff); // Solaris + len = readlink("/proc/self/path/a.out", buff, sizeof buff - 1); // Solaris } } if (len > 0) { From b5b8d9010ec2dcc78efdcaed1cb0532e4e4c4603 Mon Sep 17 00:00:00 2001 From: Laurence McGlashan Date: Wed, 13 Apr 2016 22:45:12 +0100 Subject: [PATCH 096/363] Correct typo in valgrind completions --- share/completions/valgrind.fish | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/share/completions/valgrind.fish b/share/completions/valgrind.fish index 9dfe968c1..bc805f4a8 100644 --- a/share/completions/valgrind.fish +++ b/share/completions/valgrind.fish @@ -42,8 +42,8 @@ complete -c valgrind -l demangle -xd "Demangle C++ names" -a "yes no" complete -xc valgrind -l num-callers --description "Callers in stack trace" complete -xc valgrind -l error-limit --description "Stop showing errors if too many" -a "yes no" complete -xc valgrind -l show-below-main --description "Continue trace below main()" -a "yes no" -complete -rc valgrind -l supressions --description "Supress errors from file" -complete -c valgrind -l gen-supressions --description "Print suppressions for detected errors" +complete -rc valgrind -l suppressions --description "Supress errors from file" +complete -c valgrind -l gen-suppressions --description "Print suppressions for detected errors" complete -xc valgrind -l db-attach --description "Start debugger on error" -a "yes no" complete -rc valgrind -l db-command --description "Debugger command" complete -xc valgrind -l input-fd --description "File descriptor for input" -a "0 1 2 3 4 5 6 7 8 9" From 671c0515d4212f3cf37eccc080527c42eec7d060 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Thu, 14 Apr 2016 19:05:31 -0700 Subject: [PATCH 097/363] tell git to ignore test failure artifacts --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 717e32434..2420bb4e5 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ toc.txt user_doc/ xcuserdata test/ +tests/*.tmp.* FISH-BUILD-VERSION-FILE version messages.pot From 5f849d02646b60943557b00b51a0e592c2387959 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Wed, 13 Apr 2016 19:46:18 -0700 Subject: [PATCH 098/363] provide a better experience when user presses \cC Fixes #2904 --- share/functions/__fish_cancel_commandline.fish | 13 +++++++++++++ share/functions/fish_default_key_bindings.fish | 2 +- share/functions/fish_vi_key_bindings.fish | 10 ++++------ 3 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 share/functions/__fish_cancel_commandline.fish diff --git a/share/functions/__fish_cancel_commandline.fish b/share/functions/__fish_cancel_commandline.fish new file mode 100644 index 000000000..f10be75fd --- /dev/null +++ b/share/functions/__fish_cancel_commandline.fish @@ -0,0 +1,13 @@ +# This is meant to be bound to something like \cC. +function __fish_cancel_commandline + set -l cmd (commandline) + if test -n "$cmd" + commandline -C 1000000 + echo (set_color -b bryellow black)"^C"(set_color normal) + for i in (seq (commandline -L)) + echo "" + end + commandline "" + commandline -f repaint + end +end diff --git a/share/functions/fish_default_key_bindings.fish b/share/functions/fish_default_key_bindings.fish index 8d86c7eec..0f0d578cb 100644 --- a/share/functions/fish_default_key_bindings.fish +++ b/share/functions/fish_default_key_bindings.fish @@ -109,7 +109,7 @@ function fish_default_key_bindings -d "Default (Emacs-like) key bindings for fis bind $argv \el __fish_list_current_token bind $argv \ew 'set tok (commandline -pt); if test $tok[1]; echo; whatis $tok[1]; commandline -f repaint; end' bind $argv \cl 'clear; commandline -f repaint' - bind $argv \cc 'commandline ""' + bind $argv \cc __fish_cancel_commandline bind $argv \cu backward-kill-line bind $argv \cw backward-kill-path-component bind $argv \ed 'set -l cmd (commandline); if test -z "$cmd"; echo; dirh; commandline -f repaint; else; commandline -f kill-word; end' diff --git a/share/functions/fish_vi_key_bindings.fish b/share/functions/fish_vi_key_bindings.fish index f63956677..b1fc26594 100644 --- a/share/functions/fish_vi_key_bindings.fish +++ b/share/functions/fish_vi_key_bindings.fish @@ -22,22 +22,20 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' # Remove the default self-insert bindings in default mode bind -e "" -M default # Add way to kill current command line while in insert mode. - bind -M insert \cc 'commandline ""' + bind -M insert \cc __fish_cancel_commandline # Add a way to switch from insert to normal (command) mode. bind -M insert -m default \e backward-char force-repaint - # - # normal (command) mode - # + # Default (command) mode bind :q exit bind \cd exit - bind \cc 'commandline ""' + bind -m insert \cc __fish_cancel_commandline bind h backward-char bind l forward-char bind \e\[C forward-char bind \e\[D backward-char - # Some linux VTs output these (why?) + # Some terminals output these when they're in in keypad mode. bind \eOC forward-char bind \eOD backward-char From 21e927d24e2588710f5ab2a1ea83e6f2c84639eb Mon Sep 17 00:00:00 2001 From: Yauhen Kirylau Date: Fri, 15 Apr 2016 13:45:48 +0200 Subject: [PATCH 099/363] Add completions for 'pacaur' (#2934) --- share/completions/pacaur.fish | 167 ++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100755 share/completions/pacaur.fish diff --git a/share/completions/pacaur.fish b/share/completions/pacaur.fish new file mode 100755 index 000000000..f32b65910 --- /dev/null +++ b/share/completions/pacaur.fish @@ -0,0 +1,167 @@ +# Completions for pacaur + +set -l progname pacaur +complete -e -c $progname +complete -c $progname -f + +set -l listinstalled "(pacman -Q | string replace ' ' \t)" +# This might be an issue if another package manager is also installed (e.g. for containers) +set -l listall "(__fish_print_packages)" +set -l listrepos "(__fish_print_pacman_repos)" +set -l listgroups "(pacman -Sg)\t'Package Group'" + +set -l hasopt '__fish_contains_opt -s B backup -s C -s G getpkgbuild -s P pkgbuild -s D database -s Q query -s R remove -s S sync -s U upgrade -s F files stats' +set -l noopt "not $hasopt" + +set -l backup '__fish_contains_opt -s B backup' +set -l clean '__fish_contains_opt -s C' +set -l getpkgbuild '__fish_contains_opt -s G getpkgbuild' +set -l pkgbuild '__fish_contains_opt -s P pkgbuild' +set -l database '__fish_contains_opt -s D database' +set -l query '__fish_contains_opt -s Q query' +set -l remove '__fish_contains_opt -s R remove' +set -l sync '__fish_contains_opt -s S sync' +set -l upgrade '__fish_contains_opt -s U upgrade' +set -l files '__fish_contains_opt -s F files' + +# HACK: We only need these three to coerce fish to stop file completion and complete options +complete -c $progname -n $noopt -a "-D" -d "Modify the package database" +complete -c $progname -n $noopt -a "-Q" -d "Query the package database" +complete -c $progname -n $noopt -a "-C" -d "Manage .pac* files" +# Primary operations +complete -c $progname -s D -f -l database -n $noopt -d 'Modify the package database' +complete -c $progname -s Q -f -l query -n $noopt -d 'Query the package database' +complete -c $progname -s R -f -l remove -n $noopt -d 'Remove packages from the system' +complete -c $progname -s S -f -l sync -n $noopt -d 'Synchronize packages' +complete -c $progname -s T -f -l deptest -n $noopt -d 'Check dependencies' +complete -c $progname -s U -f -l upgrade -n $noopt -d 'Upgrade or add a local package' +complete -c $progname -s F -f -l files -n $noopt -d 'Query the files database' +complete -c $progname -s V -f -l version -d 'Display version and exit' +complete -c $progname -s h -f -l help -d 'Display help' +# pacaur operations +complete -c $progname -n $noopt -s s -l search -d "(AUR) Search for packages" +complete -c $progname -n $noopt -s i -l info -d "(AUR) Show info for packages" +complete -c $progname -n $noopt -s m -l makepkg -d "(AUR) Clone the packages' build files and build them" +complete -c $progname -n $noopt -s y -l sync -d "(AUR) Clone build files, build and install packages" +complete -c $progname -n $noopt -s k -l check -d "(AUR) Check foreign packages for updates" +complete -c $progname -n $noopt -s u -l update -d "(AUR) Update foreign packages" +complete -c $progname -n "$noopt; and not __fish_contains_opt -s d download" -s d -l download -d "(AUR) Clone the packages' build files" +complete -c $progname -n "$noopt; and __fish_contains_opt -s d download" -s d -l download -d "Download dependencies recursively" + + +# General options +# Only offer these once a command has been given so they get prominent display +complete -c $progname -n $noopt -s b -l dbpath -d 'Alternative database location' -xa '(__fish_complete_directories)' +complete -c $progname -n $hasopt -s r -l root -d 'Alternative installation root' +complete -c $progname -n $hasopt -s v -l verbose -d 'Output more status messages' +complete -c $progname -n $hasopt -l arch -d 'Alternate architecture' -f +complete -c $progname -n $hasopt -l cachedir -d 'Alternative package cache location' +complete -c $progname -n $hasopt -l config -d 'Alternate config file' +complete -c $progname -n $hasopt -l debug -d 'Display debug messages' -f +complete -c $progname -n $hasopt -l gpgdir -d 'GPG directory to verify signatures' +complete -c $progname -n $hasopt -l hookdir -d 'Hook file directory' +complete -c $progname -n $hasopt -l logfile -d 'Specify alternative log file' +complete -c $progname -n $hasopt -l noconfirm -d 'Bypass any question' -f +# General options (pacaur only) +complete -c $progname -n $hasopt -s a -l aur -d 'Apply only for AUR targets' +complete -c $progname -n $hasopt -s r -l repo -d 'Apply only for specified repo' +complete -c $progname -n $hasopt -s r -l edit -d 'Edit build files' +complete -c $progname -n $hasopt -l noedit -d 'Do not edit build files' +complete -c $progname -n $hasopt -l rebuild -d 'Always rebuild packages' +complete -c $progname -n $hasopt -l silent -d 'Redirect output to the log in the clone directory' +complete -c $progname -n $hasopt -l domain -x -d 'Point at a domain other than the default aur.archlinux.org' +complete -c $progname -n $hasopt -l devel -d 'Use devel packages' +complete -c $progname -n $hasopt -l ignore-ood -d 'Ignore all results marked as out of date' +complete -c $progname -n $hasopt -l no-ignore-ood -d 'Do not ignore all results marked as out of date' +complete -c $progname -n $hasopt -l sort -d 'Sort ascending by key' -xa "name votes popularity" +complete -c $progname -n $hasopt -l rsort -d 'Sort descending by key' -xa "name votes popularity" +complete -c $progname -n $hasopt -l by -d 'Search by field' -xa "name name-desc maintainer" + +# Transaction options (sync, remove, upgrade) +for condition in sync remove upgrade + complete -c $progname -n $$condition -s d -l nodeps -d 'Skip [all] dependency checks' -f + complete -c $progname -n $$condition -l dbonly -d 'Modify database entry only' -f + complete -c $progname -n $$condition -l noprogressbar -d 'Do not display progress bar' -f + complete -c $progname -n $$condition -l noscriptlet -d 'Do not execute install script' -f + complete -c $progname -n $$condition -s p -l print -d 'Dry run, only print targets' -f + complete -c $progname -n $$condition -l print-format -x -d 'Specify printf-like format' -f +end + +# Database and upgrade options (database, sync, upgrade) +for condition in database sync upgrade + complete -c $progname -n $$condition -l asdeps -d 'Mark PACKAGE as dependency' -f + complete -c $progname -n $$condition -l asexplicit -d 'Mark PACKAGE as explicitly installed' -f +end + +# Upgrade options (sync, upgrade) +for condition in sync upgrade + complete -c $progname -n $$condition -l force -d 'Bypass file conflict checks' -f + complete -c $progname -n $$condition -l ignore -d 'Ignore upgrade of PACKAGE' -xa "$listinstalled" -f + complete -c $progname -n $$condition -l ignoregroup -d 'Ignore upgrade of GROUP' -xa "$listgroups" -f + complete -c $progname -n $$condition -l needed -d 'Do not reinstall up-to-date targets' -f + complete -c $progname -n $$condition -l recursive -d 'Recursively reinstall all dependencies' -f +end + +# Query and sync options +for condition in query sync + complete -c $progname -n $$condition -s g -l groups -d 'Display all packages in GROUP' -xa "$listgroups" -f + complete -c $progname -n $$condition -s i -l info -d 'Display information on PACKAGE' -f + complete -c $progname -n $$condition -s q -l quiet -d 'Show less information' -f + complete -c $progname -n $$condition -s s -l search -r -d 'Search packages for regexp' -f +end + +# Query options +complete -c $progname -n $query -s c -l changelog -d 'View the change log of PACKAGE' -f +complete -c $progname -n $query -s d -l deps -d 'List only non-explicit packages (dependencies)' -f +complete -c $progname -n $query -s e -l explicit -d 'List only explicitly installed packages' -f +complete -c $progname -n $query -s k -l check -d 'Check if all files owned by PACKAGE are present' -f +complete -c $progname -n $query -s l -l list -d 'List all files owned by PACKAGE' -f +complete -c $progname -n $query -s m -l foreign -d 'List all packages not in the database' -f +complete -c $progname -n $query -s o -l owns -r -d 'Search for the package that owns FILE' -xa '' -f +complete -c $progname -n $query -s p -l file -d 'Apply the query to a package file, not package' -xa '' -f +complete -c $progname -n $query -s t -l unrequired -d 'List only unrequired packages' -f +complete -c $progname -n $query -s u -l upgrades -d 'List only out-of-date packages' -f +complete -c $progname -n "$query" -d 'Installed package' -xa $listinstalled -f + +# Remove options +complete -c $progname -n $remove -s c -l cascade -d 'Also remove packages depending on PACKAGE' -f +complete -c $progname -n $remove -s n -l nosave -d 'Ignore file backup designations' -f +complete -c $progname -n $remove -s s -l recursive -d 'Also remove dependencies of PACKAGE' -f +complete -c $progname -n $remove -s u -l unneeded -d 'Only remove targets not required by PACKAGE' -f +complete -c $progname -n "$remove" -d 'Installed package' -xa $listinstalled -f + +# Sync options +complete -c $progname -n $sync -s c -l clean -d 'Remove [all] packages from cache' +complete -c $progname -n $sync -s l -l list -xa "$listrepos" -d 'List all packages in REPOSITORY' +complete -c $progname -n "$sync; and not __fish_contains_opt -s u sysupgrade" -s u -l sysupgrade -d 'Upgrade all packages that are out of date' +complete -c $progname -n "$sync; and __fish_contains_opt -s u sysupgrade" -s u -l sysupgrade -d 'Also downgrade packages' +complete -c $progname -n $sync -s w -l downloadonly -d 'Only download the target packages' +complete -c $progname -n $sync -s y -l refresh -d 'Download fresh copy of the package list' +complete -c $progname -n "$sync" -xa "$listall $listgroups" + +# Database options +set -l has_db_opt '__fish_contains_opt asdeps asexplicit' +complete -c $progname -n "$database; and not $has_db_opt" -xa --asdeps -d 'Mark PACKAGE as dependency' +complete -c $progname -n "$database; and not $has_db_opt" -xa --asexplicit -d 'Mark PACKAGE as explicitly installed' +complete -c $progname -n "$database; and not $has_db_opt" -s k -l check -d 'Check database validity' +complete -c $progname -n "$has_db_opt; and $database" -xa "$listinstalled" + +# File options - since pacman 5 +set -l has_file_opt '__fish_contains_opt list search -s l -s s' +complete -c $progname -n "$files; and not $has_file_opt" -xa --list -d 'List files owned by given packages' +complete -c $progname -n "$files; and not $has_file_opt" -xa -l -d 'List files owned by given packages' +complete -c $progname -n "$files; and not $has_file_opt" -xa --search -d 'Search packages for matching files' +complete -c $progname -n "$files; and not $has_file_opt" -xa -s -d 'Search packages for matching files' +complete -c $progname -n "$files" -s y -l refresh -d 'Refresh the files database' -f +complete -c $progname -n "$files" -s l -l list -d 'List files owned by given packages' -xa $listall +complete -c $progname -n "$files" -s s -l search -d 'Search packages for matching files' +complete -c $progname -n "$files" -s o -l owns -d 'Search for packages that include the given files' +complete -c $progname -n "$files" -s q -l quiet -d 'Show less information' -f +complete -c $progname -n "$files" -l machinereadable -d 'Show in machine readable format' -f + +# Upgrade options +# Theoretically, pacman reads packages in all formats that libarchive supports +# In practice, it's going to be tar.xz or tar.gz +# Using "pkg.tar.*" here would change __fish_complete_suffix's descriptions to "unknown" +complete -c $progname -n "$upgrade" -xa '(__fish_complete_suffix pkg.tar.xz)' -d 'Package file' +complete -c $progname -n "$upgrade" -xa '(__fish_complete_suffix pkg.tar.gz)' -d 'Package file' From 635a1e9dd24dc64077bd1d7e02bf998b41984974 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 12 Apr 2016 21:40:14 +0200 Subject: [PATCH 100/363] Remove the default self-insert binding in vi-default mode Fixes #2832. --- share/functions/fish_vi_key_bindings.fish | 2 ++ 1 file changed, 2 insertions(+) diff --git a/share/functions/fish_vi_key_bindings.fish b/share/functions/fish_vi_key_bindings.fish index 75be6644b..f63956677 100644 --- a/share/functions/fish_vi_key_bindings.fish +++ b/share/functions/fish_vi_key_bindings.fish @@ -19,6 +19,8 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' fish_default_key_bindings -M insert fish_default_key_bindings -M default + # Remove the default self-insert bindings in default mode + bind -e "" -M default # Add way to kill current command line while in insert mode. bind -M insert \cc 'commandline ""' # Add a way to switch from insert to normal (command) mode. From 4aa8fc753f9f5e39ef3cdc83d2c48fd2b353eef8 Mon Sep 17 00:00:00 2001 From: Laurence McGlashan Date: Wed, 13 Apr 2016 22:45:12 +0100 Subject: [PATCH 101/363] Correct typo in valgrind completions --- share/completions/valgrind.fish | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/share/completions/valgrind.fish b/share/completions/valgrind.fish index 9dfe968c1..bc805f4a8 100644 --- a/share/completions/valgrind.fish +++ b/share/completions/valgrind.fish @@ -42,8 +42,8 @@ complete -c valgrind -l demangle -xd "Demangle C++ names" -a "yes no" complete -xc valgrind -l num-callers --description "Callers in stack trace" complete -xc valgrind -l error-limit --description "Stop showing errors if too many" -a "yes no" complete -xc valgrind -l show-below-main --description "Continue trace below main()" -a "yes no" -complete -rc valgrind -l supressions --description "Supress errors from file" -complete -c valgrind -l gen-supressions --description "Print suppressions for detected errors" +complete -rc valgrind -l suppressions --description "Supress errors from file" +complete -c valgrind -l gen-suppressions --description "Print suppressions for detected errors" complete -xc valgrind -l db-attach --description "Start debugger on error" -a "yes no" complete -rc valgrind -l db-command --description "Debugger command" complete -xc valgrind -l input-fd --description "File descriptor for input" -a "0 1 2 3 4 5 6 7 8 9" From c2e9cda7b3f363a7441da3fb5a3917d88e50531d Mon Sep 17 00:00:00 2001 From: Yauhen Kirylau Date: Fri, 15 Apr 2016 13:45:48 +0200 Subject: [PATCH 102/363] Add completions for 'pacaur' (#2934) --- share/completions/pacaur.fish | 167 ++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100755 share/completions/pacaur.fish diff --git a/share/completions/pacaur.fish b/share/completions/pacaur.fish new file mode 100755 index 000000000..f32b65910 --- /dev/null +++ b/share/completions/pacaur.fish @@ -0,0 +1,167 @@ +# Completions for pacaur + +set -l progname pacaur +complete -e -c $progname +complete -c $progname -f + +set -l listinstalled "(pacman -Q | string replace ' ' \t)" +# This might be an issue if another package manager is also installed (e.g. for containers) +set -l listall "(__fish_print_packages)" +set -l listrepos "(__fish_print_pacman_repos)" +set -l listgroups "(pacman -Sg)\t'Package Group'" + +set -l hasopt '__fish_contains_opt -s B backup -s C -s G getpkgbuild -s P pkgbuild -s D database -s Q query -s R remove -s S sync -s U upgrade -s F files stats' +set -l noopt "not $hasopt" + +set -l backup '__fish_contains_opt -s B backup' +set -l clean '__fish_contains_opt -s C' +set -l getpkgbuild '__fish_contains_opt -s G getpkgbuild' +set -l pkgbuild '__fish_contains_opt -s P pkgbuild' +set -l database '__fish_contains_opt -s D database' +set -l query '__fish_contains_opt -s Q query' +set -l remove '__fish_contains_opt -s R remove' +set -l sync '__fish_contains_opt -s S sync' +set -l upgrade '__fish_contains_opt -s U upgrade' +set -l files '__fish_contains_opt -s F files' + +# HACK: We only need these three to coerce fish to stop file completion and complete options +complete -c $progname -n $noopt -a "-D" -d "Modify the package database" +complete -c $progname -n $noopt -a "-Q" -d "Query the package database" +complete -c $progname -n $noopt -a "-C" -d "Manage .pac* files" +# Primary operations +complete -c $progname -s D -f -l database -n $noopt -d 'Modify the package database' +complete -c $progname -s Q -f -l query -n $noopt -d 'Query the package database' +complete -c $progname -s R -f -l remove -n $noopt -d 'Remove packages from the system' +complete -c $progname -s S -f -l sync -n $noopt -d 'Synchronize packages' +complete -c $progname -s T -f -l deptest -n $noopt -d 'Check dependencies' +complete -c $progname -s U -f -l upgrade -n $noopt -d 'Upgrade or add a local package' +complete -c $progname -s F -f -l files -n $noopt -d 'Query the files database' +complete -c $progname -s V -f -l version -d 'Display version and exit' +complete -c $progname -s h -f -l help -d 'Display help' +# pacaur operations +complete -c $progname -n $noopt -s s -l search -d "(AUR) Search for packages" +complete -c $progname -n $noopt -s i -l info -d "(AUR) Show info for packages" +complete -c $progname -n $noopt -s m -l makepkg -d "(AUR) Clone the packages' build files and build them" +complete -c $progname -n $noopt -s y -l sync -d "(AUR) Clone build files, build and install packages" +complete -c $progname -n $noopt -s k -l check -d "(AUR) Check foreign packages for updates" +complete -c $progname -n $noopt -s u -l update -d "(AUR) Update foreign packages" +complete -c $progname -n "$noopt; and not __fish_contains_opt -s d download" -s d -l download -d "(AUR) Clone the packages' build files" +complete -c $progname -n "$noopt; and __fish_contains_opt -s d download" -s d -l download -d "Download dependencies recursively" + + +# General options +# Only offer these once a command has been given so they get prominent display +complete -c $progname -n $noopt -s b -l dbpath -d 'Alternative database location' -xa '(__fish_complete_directories)' +complete -c $progname -n $hasopt -s r -l root -d 'Alternative installation root' +complete -c $progname -n $hasopt -s v -l verbose -d 'Output more status messages' +complete -c $progname -n $hasopt -l arch -d 'Alternate architecture' -f +complete -c $progname -n $hasopt -l cachedir -d 'Alternative package cache location' +complete -c $progname -n $hasopt -l config -d 'Alternate config file' +complete -c $progname -n $hasopt -l debug -d 'Display debug messages' -f +complete -c $progname -n $hasopt -l gpgdir -d 'GPG directory to verify signatures' +complete -c $progname -n $hasopt -l hookdir -d 'Hook file directory' +complete -c $progname -n $hasopt -l logfile -d 'Specify alternative log file' +complete -c $progname -n $hasopt -l noconfirm -d 'Bypass any question' -f +# General options (pacaur only) +complete -c $progname -n $hasopt -s a -l aur -d 'Apply only for AUR targets' +complete -c $progname -n $hasopt -s r -l repo -d 'Apply only for specified repo' +complete -c $progname -n $hasopt -s r -l edit -d 'Edit build files' +complete -c $progname -n $hasopt -l noedit -d 'Do not edit build files' +complete -c $progname -n $hasopt -l rebuild -d 'Always rebuild packages' +complete -c $progname -n $hasopt -l silent -d 'Redirect output to the log in the clone directory' +complete -c $progname -n $hasopt -l domain -x -d 'Point at a domain other than the default aur.archlinux.org' +complete -c $progname -n $hasopt -l devel -d 'Use devel packages' +complete -c $progname -n $hasopt -l ignore-ood -d 'Ignore all results marked as out of date' +complete -c $progname -n $hasopt -l no-ignore-ood -d 'Do not ignore all results marked as out of date' +complete -c $progname -n $hasopt -l sort -d 'Sort ascending by key' -xa "name votes popularity" +complete -c $progname -n $hasopt -l rsort -d 'Sort descending by key' -xa "name votes popularity" +complete -c $progname -n $hasopt -l by -d 'Search by field' -xa "name name-desc maintainer" + +# Transaction options (sync, remove, upgrade) +for condition in sync remove upgrade + complete -c $progname -n $$condition -s d -l nodeps -d 'Skip [all] dependency checks' -f + complete -c $progname -n $$condition -l dbonly -d 'Modify database entry only' -f + complete -c $progname -n $$condition -l noprogressbar -d 'Do not display progress bar' -f + complete -c $progname -n $$condition -l noscriptlet -d 'Do not execute install script' -f + complete -c $progname -n $$condition -s p -l print -d 'Dry run, only print targets' -f + complete -c $progname -n $$condition -l print-format -x -d 'Specify printf-like format' -f +end + +# Database and upgrade options (database, sync, upgrade) +for condition in database sync upgrade + complete -c $progname -n $$condition -l asdeps -d 'Mark PACKAGE as dependency' -f + complete -c $progname -n $$condition -l asexplicit -d 'Mark PACKAGE as explicitly installed' -f +end + +# Upgrade options (sync, upgrade) +for condition in sync upgrade + complete -c $progname -n $$condition -l force -d 'Bypass file conflict checks' -f + complete -c $progname -n $$condition -l ignore -d 'Ignore upgrade of PACKAGE' -xa "$listinstalled" -f + complete -c $progname -n $$condition -l ignoregroup -d 'Ignore upgrade of GROUP' -xa "$listgroups" -f + complete -c $progname -n $$condition -l needed -d 'Do not reinstall up-to-date targets' -f + complete -c $progname -n $$condition -l recursive -d 'Recursively reinstall all dependencies' -f +end + +# Query and sync options +for condition in query sync + complete -c $progname -n $$condition -s g -l groups -d 'Display all packages in GROUP' -xa "$listgroups" -f + complete -c $progname -n $$condition -s i -l info -d 'Display information on PACKAGE' -f + complete -c $progname -n $$condition -s q -l quiet -d 'Show less information' -f + complete -c $progname -n $$condition -s s -l search -r -d 'Search packages for regexp' -f +end + +# Query options +complete -c $progname -n $query -s c -l changelog -d 'View the change log of PACKAGE' -f +complete -c $progname -n $query -s d -l deps -d 'List only non-explicit packages (dependencies)' -f +complete -c $progname -n $query -s e -l explicit -d 'List only explicitly installed packages' -f +complete -c $progname -n $query -s k -l check -d 'Check if all files owned by PACKAGE are present' -f +complete -c $progname -n $query -s l -l list -d 'List all files owned by PACKAGE' -f +complete -c $progname -n $query -s m -l foreign -d 'List all packages not in the database' -f +complete -c $progname -n $query -s o -l owns -r -d 'Search for the package that owns FILE' -xa '' -f +complete -c $progname -n $query -s p -l file -d 'Apply the query to a package file, not package' -xa '' -f +complete -c $progname -n $query -s t -l unrequired -d 'List only unrequired packages' -f +complete -c $progname -n $query -s u -l upgrades -d 'List only out-of-date packages' -f +complete -c $progname -n "$query" -d 'Installed package' -xa $listinstalled -f + +# Remove options +complete -c $progname -n $remove -s c -l cascade -d 'Also remove packages depending on PACKAGE' -f +complete -c $progname -n $remove -s n -l nosave -d 'Ignore file backup designations' -f +complete -c $progname -n $remove -s s -l recursive -d 'Also remove dependencies of PACKAGE' -f +complete -c $progname -n $remove -s u -l unneeded -d 'Only remove targets not required by PACKAGE' -f +complete -c $progname -n "$remove" -d 'Installed package' -xa $listinstalled -f + +# Sync options +complete -c $progname -n $sync -s c -l clean -d 'Remove [all] packages from cache' +complete -c $progname -n $sync -s l -l list -xa "$listrepos" -d 'List all packages in REPOSITORY' +complete -c $progname -n "$sync; and not __fish_contains_opt -s u sysupgrade" -s u -l sysupgrade -d 'Upgrade all packages that are out of date' +complete -c $progname -n "$sync; and __fish_contains_opt -s u sysupgrade" -s u -l sysupgrade -d 'Also downgrade packages' +complete -c $progname -n $sync -s w -l downloadonly -d 'Only download the target packages' +complete -c $progname -n $sync -s y -l refresh -d 'Download fresh copy of the package list' +complete -c $progname -n "$sync" -xa "$listall $listgroups" + +# Database options +set -l has_db_opt '__fish_contains_opt asdeps asexplicit' +complete -c $progname -n "$database; and not $has_db_opt" -xa --asdeps -d 'Mark PACKAGE as dependency' +complete -c $progname -n "$database; and not $has_db_opt" -xa --asexplicit -d 'Mark PACKAGE as explicitly installed' +complete -c $progname -n "$database; and not $has_db_opt" -s k -l check -d 'Check database validity' +complete -c $progname -n "$has_db_opt; and $database" -xa "$listinstalled" + +# File options - since pacman 5 +set -l has_file_opt '__fish_contains_opt list search -s l -s s' +complete -c $progname -n "$files; and not $has_file_opt" -xa --list -d 'List files owned by given packages' +complete -c $progname -n "$files; and not $has_file_opt" -xa -l -d 'List files owned by given packages' +complete -c $progname -n "$files; and not $has_file_opt" -xa --search -d 'Search packages for matching files' +complete -c $progname -n "$files; and not $has_file_opt" -xa -s -d 'Search packages for matching files' +complete -c $progname -n "$files" -s y -l refresh -d 'Refresh the files database' -f +complete -c $progname -n "$files" -s l -l list -d 'List files owned by given packages' -xa $listall +complete -c $progname -n "$files" -s s -l search -d 'Search packages for matching files' +complete -c $progname -n "$files" -s o -l owns -d 'Search for packages that include the given files' +complete -c $progname -n "$files" -s q -l quiet -d 'Show less information' -f +complete -c $progname -n "$files" -l machinereadable -d 'Show in machine readable format' -f + +# Upgrade options +# Theoretically, pacman reads packages in all formats that libarchive supports +# In practice, it's going to be tar.xz or tar.gz +# Using "pkg.tar.*" here would change __fish_complete_suffix's descriptions to "unknown" +complete -c $progname -n "$upgrade" -xa '(__fish_complete_suffix pkg.tar.xz)' -d 'Package file' +complete -c $progname -n "$upgrade" -xa '(__fish_complete_suffix pkg.tar.gz)' -d 'Package file' From baee8078377109bec778b77b061a56d240c34eb5 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Tue, 12 Apr 2016 19:03:07 -0700 Subject: [PATCH 103/363] fish handling of readlink() The readlink() function does not null terminate the path it returns. Remove the OS X code that deals with a path buffer that is too short. For one thing a loop isn't needed since we're told how big of a buffer is required if the first _NSGetExecutablePath() call fails. But more important it is so unlikely that the path will be longer than PATH_MAX that if it is we should just give up. Fixes 2931. (cherry picked from commit 8e103c231e74a3dda40d0a0e5a844ea8b6aa1e4b) --- src/fish.cpp | 49 ++++++++++++++++++++++--------------------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/src/fish.cpp b/src/fish.cpp index 17cd1f16a..bde7854ee 100644 --- a/src/fish.cpp +++ b/src/fish.cpp @@ -92,39 +92,34 @@ extern "C" { int _NSGetExecutablePath(char* buf, uint32_t* bufsize); } -/* Return the path to the current executable. This needs to be realpath'd. */ +// Return the path to the current executable. This needs to be realpath'd. static std::string get_executable_path(const char *argv0) { - char buff[PATH_MAX]; + char buff[PATH_MAX + 1]; + #if __APPLE__ - { - /* Returns 0 on success, -1 if the buffer is too small */ - 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); - return result; - } -#endif - { - /* 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 - { - return std::string(buff); + // On OS X use it's proprietary API to get the path to the executable. + uint32_t buffSize = sizeof buff; + if (_NSGetExecutablePath(buff, &buffSize) == 0) return std::string(buff); +#else + // On non-OS X UNIXes, try /proc directory. + ssize_t len; + len = readlink("/proc/self/exe", buff, sizeof buff); // Linux + if (len == -1) { + len = readlink("/proc/curproc/file", buff, sizeof buff); // BSD + if (len == -1) { + len = readlink("/proc/self/path/a.out", buff, sizeof buff); // Solaris } } + if (len > 0) { + buff[len] = '\0'; + return std::string(buff); + } +#endif - /* 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. */ + // 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 : ""); } From d7fd0427f36c5cb761bd61875d44d58329156660 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Wed, 13 Apr 2016 09:00:07 -0700 Subject: [PATCH 104/363] fix off by one error (cherry picked from commit ea3d9c36a52dcf6bcde0ff41af8ca19daca2267e) --- src/fish.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/fish.cpp b/src/fish.cpp index bde7854ee..9676814de 100644 --- a/src/fish.cpp +++ b/src/fish.cpp @@ -62,9 +62,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA #include "input_common.h" #include "wildcard.h" -/* PATH_MAX may not exist */ +// PATH_MAX may not exist. #ifndef PATH_MAX -#define PATH_MAX 1024 +#define PATH_MAX 4096 #endif /* If we are doing profiling, the filename to output to */ @@ -95,7 +95,7 @@ extern "C" { // Return the path to the current executable. This needs to be realpath'd. static std::string get_executable_path(const char *argv0) { - char buff[PATH_MAX + 1]; + char buff[PATH_MAX]; #if __APPLE__ // On OS X use it's proprietary API to get the path to the executable. @@ -104,11 +104,11 @@ static std::string get_executable_path(const char *argv0) #else // On non-OS X UNIXes, try /proc directory. ssize_t len; - len = readlink("/proc/self/exe", buff, sizeof buff); // Linux + len = readlink("/proc/self/exe", buff, sizeof buff - 1); // Linux if (len == -1) { - len = readlink("/proc/curproc/file", buff, sizeof buff); // BSD + len = readlink("/proc/curproc/file", buff, sizeof buff - 1); // BSD if (len == -1) { - len = readlink("/proc/self/path/a.out", buff, sizeof buff); // Solaris + len = readlink("/proc/self/path/a.out", buff, sizeof buff - 1); // Solaris } } if (len > 0) { From f034d8ba3a78b544f48bb72f8db06a047481f1d2 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Thu, 14 Apr 2016 18:37:19 -0700 Subject: [PATCH 105/363] number `dirh` output to make prevd/nextd easier Fixes #2786 --- share/functions/dirh.fish | 56 +++++++++++++++------------------------ 1 file changed, 22 insertions(+), 34 deletions(-) diff --git a/share/functions/dirh.fish b/share/functions/dirh.fish index 44244e8c9..942b1ed95 100644 --- a/share/functions/dirh.fish +++ b/share/functions/dirh.fish @@ -1,38 +1,26 @@ - -function dirh --description "Print the current directory history (the back- and fwd- lists)" - - if count $argv >/dev/null - switch $argv[1] - case -h --h --he --hel --help - __fish_print_help dirh - return 0 - end - end - - # Avoid set comment - set -l current (command pwd) - set -l separator " " - set -l line_len (math (count $dirprev) + (echo $dirprev $current $dirnext | wc -m) ) - if test $line_len -gt $COLUMNS - # Print one entry per line if history is long - set separator "\n" - end - - for i in $dirprev - echo -n -e $i$separator - end - - set_color $fish_color_history_current - echo -n -e $current$separator - set_color normal - - # BSD seq 0 outputs '1 0' instead of nothing - if count $dirnext > /dev/null - for i in (seq (echo (count $dirnext)) -1 1) - echo -n -e $dirnext[$i]$separator +function dirh --description "Print the current directory history (the prev and next lists)" + if set -q argv[1] + switch $argv[1] + case -h --h --he --hel --help + __fish_print_help dirh + return 0 end end - echo -end + set -l dirc (count $dirprev) + set -l dirprev_rev $dirprev[-1..1] + for i in (seq $dirc -1 1) + printf '%2d) %s\n' $i $dirprev_rev[$i] + end + echo (set_color $fish_color_history_current)' ' $PWD(set_color normal) + + set -l dirc (count $dirnext) + if test $dirc -gt 0 + for i in (seq $dirc) + printf '%2d) %s\n' $i $dirnext[$i] + end + end + + echo +end From 8558561650995b330a1ae5ee87c592382856cc40 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 16 Apr 2016 13:00:14 +0200 Subject: [PATCH 106/363] Move 24bit setup into config.fish Fixes #2941. --- share/config.fish | 24 ++++++++++++++----- .../functions/__fish_config_interactive.fish | 11 --------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/share/config.fish b/share/config.fish index 86e8d9b7d..fa1a26282 100644 --- a/share/config.fish +++ b/share/config.fish @@ -17,12 +17,24 @@ function __fish_default_command_not_found_handler echo "fish: Unknown command '$argv'" >&2 end -# -# Hook up the default as the principal command_not_found handler -# in case we are not interactive -# -status -i; or function __fish_command_not_found_handler --on-event fish_command_not_found - __fish_default_command_not_found_handler $argv +if status --is-interactive + # Enable truecolor/24-bit support for select terminals + if not set -q NVIM_LISTEN_ADDRESS # Neovim will swallow the 24bit sequences, rendering text white + and begin + set -q KONSOLE_PROFILE_NAME # KDE's konsole + or string match -q -- "*:*" $ITERM_SESSION_ID # Supporting versions of iTerm2 will include a colon here + or string match -q -- "st-*" $TERM # suckless' st + or test "$VTE_VERSION" -ge 3600 # Should be all gtk3-vte-based terms after version 3.6.0.0 + or test "$COLORTERM" = truecolor -o "$COLORTERM" = 24bit # slang expects this + end + set -g fish_term24bit 1 + end +else + # Hook up the default as the principal command_not_found handler + # in case we are not interactive + function __fish_command_not_found_handler --on-event fish_command_not_found + __fish_default_command_not_found_handler $argv + end end # diff --git a/share/functions/__fish_config_interactive.fish b/share/functions/__fish_config_interactive.fish index 3702c075d..bca410ac5 100644 --- a/share/functions/__fish_config_interactive.fish +++ b/share/functions/__fish_config_interactive.fish @@ -287,15 +287,4 @@ function __fish_config_interactive -d "Initializations that should be performed fish_fallback_prompt end end - - if not set -q NVIM_LISTEN_ADDRESS # Neovim will swallow the 24bit sequences, rendering text white - and begin - set -q KONSOLE_PROFILE_NAME # KDE's konsole - or string match -q -- "*:*" $ITERM_SESSION_ID # Supporting versions of iTerm2 will include a colon here - or string match -q -- "st-*" $TERM # suckless' st - or test "$VTE_VERSION" -ge 3600 # Should be all gtk3-vte-based terms after version 3.6.0.0 - or test "$COLORTERM" = truecolor -o "$COLORTERM" = 24bit # slang expects this - end - set -g fish_term24bit 1 - end end From ba1008b75033e9ff8f4ad17fa2de25eed115de5c Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 16 Apr 2016 13:00:58 +0200 Subject: [PATCH 107/363] Allow overriding fish_term24bit on launch --- share/config.fish | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/share/config.fish b/share/config.fish index fa1a26282..35de799b2 100644 --- a/share/config.fish +++ b/share/config.fish @@ -27,7 +27,8 @@ if status --is-interactive or test "$VTE_VERSION" -ge 3600 # Should be all gtk3-vte-based terms after version 3.6.0.0 or test "$COLORTERM" = truecolor -o "$COLORTERM" = 24bit # slang expects this end - set -g fish_term24bit 1 + # Only set it if it isn't to allow override by setting to 0 + set -q fish_term24bit; or set -g fish_term24bit 1 end else # Hook up the default as the principal command_not_found handler From 8d3eae0d76a29f54d3c736b64e001eed98a50926 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 16 Apr 2016 13:00:14 +0200 Subject: [PATCH 108/363] Move 24bit setup into config.fish Fixes #2941. (cherry picked from commit 8558561650995b330a1ae5ee87c592382856cc40) --- share/config.fish | 24 ++++++++++++++----- .../functions/__fish_config_interactive.fish | 11 --------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/share/config.fish b/share/config.fish index 86e8d9b7d..fa1a26282 100644 --- a/share/config.fish +++ b/share/config.fish @@ -17,12 +17,24 @@ function __fish_default_command_not_found_handler echo "fish: Unknown command '$argv'" >&2 end -# -# Hook up the default as the principal command_not_found handler -# in case we are not interactive -# -status -i; or function __fish_command_not_found_handler --on-event fish_command_not_found - __fish_default_command_not_found_handler $argv +if status --is-interactive + # Enable truecolor/24-bit support for select terminals + if not set -q NVIM_LISTEN_ADDRESS # Neovim will swallow the 24bit sequences, rendering text white + and begin + set -q KONSOLE_PROFILE_NAME # KDE's konsole + or string match -q -- "*:*" $ITERM_SESSION_ID # Supporting versions of iTerm2 will include a colon here + or string match -q -- "st-*" $TERM # suckless' st + or test "$VTE_VERSION" -ge 3600 # Should be all gtk3-vte-based terms after version 3.6.0.0 + or test "$COLORTERM" = truecolor -o "$COLORTERM" = 24bit # slang expects this + end + set -g fish_term24bit 1 + end +else + # Hook up the default as the principal command_not_found handler + # in case we are not interactive + function __fish_command_not_found_handler --on-event fish_command_not_found + __fish_default_command_not_found_handler $argv + end end # diff --git a/share/functions/__fish_config_interactive.fish b/share/functions/__fish_config_interactive.fish index 3702c075d..bca410ac5 100644 --- a/share/functions/__fish_config_interactive.fish +++ b/share/functions/__fish_config_interactive.fish @@ -287,15 +287,4 @@ function __fish_config_interactive -d "Initializations that should be performed fish_fallback_prompt end end - - if not set -q NVIM_LISTEN_ADDRESS # Neovim will swallow the 24bit sequences, rendering text white - and begin - set -q KONSOLE_PROFILE_NAME # KDE's konsole - or string match -q -- "*:*" $ITERM_SESSION_ID # Supporting versions of iTerm2 will include a colon here - or string match -q -- "st-*" $TERM # suckless' st - or test "$VTE_VERSION" -ge 3600 # Should be all gtk3-vte-based terms after version 3.6.0.0 - or test "$COLORTERM" = truecolor -o "$COLORTERM" = 24bit # slang expects this - end - set -g fish_term24bit 1 - end end From e7599fd18cba9fde4ed19aae70c10e622d7d2e0e Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 16 Apr 2016 13:00:58 +0200 Subject: [PATCH 109/363] Allow overriding fish_term24bit on launch (cherry picked from commit ba1008b75033e9ff8f4ad17fa2de25eed115de5c) --- share/config.fish | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/share/config.fish b/share/config.fish index fa1a26282..35de799b2 100644 --- a/share/config.fish +++ b/share/config.fish @@ -27,7 +27,8 @@ if status --is-interactive or test "$VTE_VERSION" -ge 3600 # Should be all gtk3-vte-based terms after version 3.6.0.0 or test "$COLORTERM" = truecolor -o "$COLORTERM" = 24bit # slang expects this end - set -g fish_term24bit 1 + # Only set it if it isn't to allow override by setting to 0 + set -q fish_term24bit; or set -g fish_term24bit 1 end else # Hook up the default as the principal command_not_found handler From 61c0ca9dd97d9aef017fd9e59f1f3176ef0e9f91 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sun, 17 Apr 2016 22:47:37 -0700 Subject: [PATCH 110/363] restyle history code to match project style Make the history code conform to the new style guide. Every change was produced by clang-format (e.g., `make style`) with the exception of comments which were manually reformatted. That has to be done by hand since clang-format leaves comments alone other than to reflow comment lines to get them below the allowed line length. The total number of lines is reduced by 313 lines (13%) in the two affected files. Line count is generally a poor metric but in this case it reflects an increase in information density without a loss in readability. Furthermore, the standardization of braces, whitespace, and comment style will make it easier for people to read the code. This reduces the number of warnings by `make lint` from 168 to 87 (a 48% decrease). Making it much easier to focus on the substantive lint issues. Further improvements are possible. For example, many comments are not very helpful (e.g., they point out the obvious) or provide insufficient detail. But those are beyond the scope of this change. This is the first step in resolving issue #2902. --- src/history.cpp | 1740 ++++++++++++++++++++--------------------------- src/history.h | 336 +++++---- 2 files changed, 882 insertions(+), 1194 deletions(-) diff --git a/src/history.cpp b/src/history.cpp index ff99db9a0..af5d062ef 100644 --- a/src/history.cpp +++ b/src/history.cpp @@ -1,159 +1,133 @@ -/** \file history.c - History functions, part of the user interface. -*/ -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +// History functions, part of the user interface. +#include "history.h" #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include #include - -#include "fallback.h" // IWYU pragma: keep -#include "sanity.h" -#include "reader.h" -#include "parse_tree.h" -#include "wutil.h" -#include "history.h" +#include #include "common.h" -#include "path.h" -#include "signal.h" -#include "iothread.h" +#include "config.h" #include "env.h" +#include "fallback.h" // IWYU pragma: keep +#include "iothread.h" #include "lru.h" #include "parse_constants.h" -#include -#include +#include "parse_tree.h" +#include "path.h" +#include "reader.h" +#include "sanity.h" +#include "signal.h" +#include "wutil.h" -/* +// Our history format is intended to be valid YAML. Here it is: +// +// - cmd: ssh blah blah blah +// when: 2348237 +// paths: +// - /path/to/something +// - /path/to/something_else +// +// Newlines are replaced by \n. Backslashes are replaced by \\. -Our history format is intended to be valid YAML. Here it is: - - - cmd: ssh blah blah blah - when: 2348237 - paths: - - /path/to/something - - /path/to/something_else - - Newlines are replaced by \n. Backslashes are replaced by \\. -*/ - -/** When we rewrite the history, the number of items we keep */ +// When we rewrite the history, the number of items we keep. #define HISTORY_SAVE_MAX (1024 * 256) -/** Whether we print timing information */ +// Whether we print timing information. #define LOG_TIMES 0 -/** Default buffer size for flushing to the history file */ -#define HISTORY_OUTPUT_BUFFER_SIZE (4096 * 4) +// Default buffer size for flushing to the history file. +#define HISTORY_OUTPUT_BUFFER_SIZE (16 * 1024) -namespace -{ +namespace { -/* Helper class for certain output. This is basically a string that allows us to ensure we only flush at record boundaries, and avoids the copying of ostringstream. Have you ever tried to implement your own streambuf? Total insanity. */ -class history_output_buffer_t -{ - /* A null-terminated C string */ +// Helper class for certain output. This is basically a string that allows us to ensure we only +// flush at record boundaries, and avoids the copying of ostringstream. Have you ever tried to +// implement your own streambuf? Total insanity. +class history_output_buffer_t { + // A null-terminated C string. std::vector buffer; - /* Offset is the offset of the null terminator */ + // Offset is the offset of the null terminator. size_t offset; - static size_t safe_strlen(const char *s) - { - return s ? strlen(s) : 0; - } + static size_t safe_strlen(const char *s) { return s ? strlen(s) : 0; } -public: + public: + // Add a bit more to HISTORY_OUTPUT_BUFFER_SIZE because we flush once we've exceeded that size. + history_output_buffer_t() : buffer(HISTORY_OUTPUT_BUFFER_SIZE + 128, '\0'), offset(0) {} - /* Add a bit more to HISTORY_OUTPUT_BUFFER_SIZE because we flush once we've exceeded that size */ - history_output_buffer_t() : buffer(HISTORY_OUTPUT_BUFFER_SIZE + 128, '\0'), offset(0) - { - } - - /* Append one or more strings */ - void append(const char *s1, const char *s2 = NULL, const char *s3 = NULL) - { + // Append one or more strings. + void append(const char *s1, const char *s2 = NULL, const char *s3 = NULL) { const char *ptrs[4] = {s1, s2, s3, NULL}; const size_t lengths[4] = {safe_strlen(s1), safe_strlen(s2), safe_strlen(s3), 0}; - /* Determine the additional size we'll need */ + // Determine the additional size we'll need. size_t additional_length = 0; - for (size_t i=0; i < sizeof lengths / sizeof *lengths; i++) - { + for (size_t i = 0; i < sizeof lengths / sizeof *lengths; i++) { additional_length += lengths[i]; } - /* Allocate that much, plus a null terminator */ + // Allocate that much, plus a null terminator. size_t required_size = offset + additional_length + 1; - if (required_size > buffer.size()) - { + if (required_size > buffer.size()) { buffer.resize(required_size, '\0'); } - /* Copy */ - for (size_t i=0; ptrs[i] != NULL; i++) - { + // Copy. + for (size_t i = 0; ptrs[i] != NULL; i++) { memmove(&buffer.at(offset), ptrs[i], lengths[i]); offset += lengths[i]; } - /* Null terminator was appended by virtue of the resize() above (or in a previous invocation). */ + // Null terminator was appended by virtue of the resize() above (or in a previous + // invocation). assert(buffer.at(buffer.size() - 1) == '\0'); } - /* Output to a given fd, resetting our buffer. Returns true on success, false on error */ - bool flush_to_fd(int fd) - { + // Output to a given fd, resetting our buffer. Returns true on success, false on error. + bool flush_to_fd(int fd) { bool result = write_loop(fd, &buffer.at(0), offset) >= 0; offset = 0; return result; } - /* Return how much data we've accumulated */ - size_t output_size() const - { - return offset; - } + // Return how much data we've accumulated. + size_t output_size() const { return offset; } }; -class time_profiler_t -{ +class time_profiler_t { const char *what; double start; -public: - explicit time_profiler_t(const char *w) - { - if (LOG_TIMES) - { + public: + explicit 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(); fprintf(stderr, "(LOG_TIMES %s: %02f msec)\n", what, (end - start) * 1000); } } }; -/* Lock a file via fcntl; returns true on success, false on failure. */ -static bool history_file_lock(int fd, short type) -{ +// Lock a file via fcntl; returns true on success, false on failure. +static bool history_file_lock(int fd, short type) { assert(type == F_RDLCK || type == F_WRLCK); struct flock flk = {}; flk.l_type = type; @@ -162,101 +136,84 @@ static bool history_file_lock(int fd, short type) return ret != -1; } -/* 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: +// 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: time_t timestamp; path_list_t required_paths; - explicit history_lru_node_t(const history_item_t &item) : - lru_node_t(item.str()), - timestamp(item.timestamp()), - required_paths(item.get_required_paths()) - {} + explicit history_lru_node_t(const history_item_t &item) + : lru_node_t(item.str()), + timestamp(item.timestamp()), + required_paths(item.get_required_paths()) {} }; -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) { delete node; } - /* Override to delete evicted nodes */ - virtual void node_was_evicted(history_lru_node_t *node) - { - delete node; - } + public: + explicit history_lru_cache_t(size_t max) : lru_cache_t(max) {} -public: - explicit history_lru_cache_t(size_t max) : lru_cache_t(max) { } + // Function to add a history item. + void add_item(const history_item_t &item) { + // Skip empty items. + if (item.empty()) return; - /* Function to add a history item */ - void add_item(const history_item_t &item) - { - /* Skip empty items */ - if (item.empty()) - return; - - /* See if it's in the cache. If it is, update the timestamp. If not, we create a new node and add it. Note that calling get_node promotes the node to the front. */ + // 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 - { + // What to do about paths here? Let's just ignore them. + } else { node = new history_lru_node_t(item); this->add_node(node); } } }; -class history_collection_t -{ +class history_collection_t { pthread_mutex_t m_lock; std::map m_histories; -public: - history_collection_t() - { - VOMIT_ON_FAILURE_NO_ERRNO(pthread_mutex_init(&m_lock, NULL)); - } - ~history_collection_t() - { - for (std::map::const_iterator i = m_histories.begin(); i != m_histories.end(); ++i) - { + public: + history_collection_t() { VOMIT_ON_FAILURE_NO_ERRNO(pthread_mutex_init(&m_lock, NULL)); } + ~history_collection_t() { + for (std::map::const_iterator i = m_histories.begin(); + i != m_histories.end(); ++i) { delete i->second; } pthread_mutex_destroy(&m_lock); } - history_t& alloc(const wcstring &name); + history_t &alloc(const wcstring &name); void save(); }; -} //anonymous namespace +} // anonymous namespace static history_collection_t histories; static wcstring history_filename(const wcstring &name, const wcstring &suffix); -/** Replaces newlines with a literal backslash followed by an n, and replaces backslashes with two backslashes. */ +// Replaces newlines with a literal backslash followed by an n, and replaces backslashes with two +// backslashes. static void escape_yaml(std::string *str); -/** Undoes escape_yaml */ +// Inverse of escape_yaml. static void unescape_yaml(std::string *str); -/* We can merge two items if they are the same command. We use the more recent timestamp, more recent identifier, and the longer list of required paths. */ -bool history_item_t::merge(const history_item_t &item) -{ +// We can merge two items if they are the same command. We use the more recent timestamp, more +// recent identifier, and the longer list of required paths. +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; } - if (this->identifier < item.identifier) - { + if (this->identifier < item.identifier) { this->identifier = item.identifier; } result = true; @@ -264,50 +221,48 @@ bool history_item_t::merge(const history_item_t &item) return result; } -history_item_t::history_item_t(const wcstring &str) : contents(str), creation_timestamp(time(NULL)), identifier(0) -{ -} +history_item_t::history_item_t(const wcstring &str) + : contents(str), creation_timestamp(time(NULL)), identifier(0) {} -history_item_t::history_item_t(const wcstring &str, time_t when, history_identifier_t ident) : contents(str), creation_timestamp(when), identifier(ident) -{ -} +history_item_t::history_item_t(const wcstring &str, time_t when, history_identifier_t ident) + : contents(str), creation_timestamp(when), identifier(ident) {} -bool history_item_t::matches_search(const wcstring &term, enum history_search_type_t type) const -{ - switch (type) - { - - case HISTORY_SEARCH_TYPE_CONTAINS: - /* We consider equal strings to NOT match a contains search (so that you don't have to see history equal to what you typed). The length check ensures that. */ +bool history_item_t::matches_search(const wcstring &term, enum history_search_type_t type) const { + switch (type) { + case HISTORY_SEARCH_TYPE_CONTAINS: { + // We consider equal strings to NOT match a contains search (so that you don't have to + // see history equal to what you typed). The length check ensures that. return contents.size() > term.size() && contents.find(term) != wcstring::npos; - - case HISTORY_SEARCH_TYPE_PREFIX: - /* We consider equal strings to match a prefix search, so that autosuggest will allow suggesting what you've typed */ + } + 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: + } + default: { sanity_lose(); return false; + } } } -/* Append our YAML history format to the provided vector at the given offset, updating the offset */ -static void append_yaml_to_buffer(const wcstring &wcmd, time_t timestamp, const path_list_t &required_paths, history_output_buffer_t *buffer) -{ +// Append our YAML history format to the provided vector at the given offset, updating the offset. +static void append_yaml_to_buffer(const wcstring &wcmd, time_t timestamp, + const path_list_t &required_paths, + history_output_buffer_t *buffer) { std::string cmd = wcs2string(wcmd); escape_yaml(&cmd); buffer->append("- cmd: ", cmd.c_str(), "\n"); char timestamp_str[96]; - snprintf(timestamp_str, sizeof timestamp_str, "%ld", (long) timestamp); + snprintf(timestamp_str, sizeof timestamp_str, "%ld", (long)timestamp); buffer->append(" when: ", timestamp_str, "\n"); - if (! required_paths.empty()) - { + if (!required_paths.empty()) { buffer->append(" paths:\n"); - 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); buffer->append(" - ", path.c_str(), "\n"); @@ -316,157 +271,143 @@ static void append_yaml_to_buffer(const wcstring &wcmd, time_t timestamp, 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) -{ +// 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) { const char *cursor = str; - /* Advance past spaces */ - while (*cursor == ' ') - cursor++; + // Advance past spaces. + while (*cursor == ' ') cursor++; - /* Look for "when:" */ + // Look for "when:". size_t when_len = 5; - if (strncmp(cursor, "when:", when_len) != 0) - return false; + if (strncmp(cursor, "when:", when_len) != 0) return false; cursor += when_len; - /* Advance past spaces */ - while (*cursor == ' ') - cursor++; + // Advance past spaces. + while (*cursor == ' ') cursor++; - /* Try to parse a timestamp. */ + // 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; } return false; } -// 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) -{ - /* Handle the hopeless case */ - if (length < 1) - return NULL; +// 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) { + // Handle the hopeless case. + if (length < 1) return NULL; - /* Get a pointer to the end, that we must not pass */ - const char * const end = start + length; + // Get a pointer to the end, that we must not pass. + const char *const end = start + length; - /* Skip past the next newline */ + // 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) - { + // Skip past the newline character itself. + if (++nextline >= end) { return NULL; } - /* Make sure this new line is itself "newline terminated". If it's not, 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; } - /* Done */ return nextline; } -// Support for iteratively locating the offsets of history items +// Support for iteratively locating the offsets of history items. // Pass the address and length of a mapped region. -// Pass a pointer to a cursor size_t, initially 0 -// If custoff_timestamp is nonzero, skip items created at or after that timestamp -// Returns (size_t)(-1) when done -static size_t offset_of_next_item_fish_2_0(const char *begin, size_t mmap_length, size_t *inout_cursor, time_t cutoff_timestamp) -{ +// Pass a pointer to a cursor size_t, initially 0. +// If custoff_timestamp is nonzero, skip items created at or after that timestamp. +// Returns (size_t)(-1) when done. +static size_t offset_of_next_item_fish_2_0(const char *begin, size_t mmap_length, + size_t *inout_cursor, time_t cutoff_timestamp) { size_t cursor = *inout_cursor; size_t result = (size_t)(-1); - while (cursor < mmap_length) - { + while (cursor < mmap_length) { const char *line_start = begin + cursor; - /* Advance the cursor to the next line */ + // Advance the cursor to the next line. const char *newline = (const char *)memchr(line_start, '\n', mmap_length - cursor); - if (newline == NULL) - break; + if (newline == NULL) break; - /* Advance the cursor past this line. +1 is for the newline */ + // Advance the cursor past this line. +1 is for the newline. cursor = newline - begin + 1; - /* Skip lines with a leading space, since these are in the interior of one of our items */ - if (line_start[0] == ' ') + // Skip lines with a leading space, since these are in the interior of one of our items. + if (line_start[0] == ' ') continue; + + // Skip very short lines to make one of the checks below easier. + if (newline - line_start < 3) continue; + + // 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)) continue; - /* Skip very short lines to make one of the checks below easier */ - if (newline - line_start < 3) - continue; - - /* 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)) - continue; - - - /* Hackish: fish 1.x rewriting a fish 2.0 history file can produce lines with lots of leading "- cmd: - cmd: - cmd:". Trim all but one leading "- cmd:". */ + // Hackish: fish 1.x rewriting a fish 2.0 history file can produce lines with lots of + // leading "- cmd: - cmd: - cmd:". Trim all but one leading "- cmd:". const char *double_cmd = "- cmd: - cmd: "; const size_t double_cmd_len = strlen(double_cmd); - while (newline - line_start > double_cmd_len && ! memcmp(line_start, double_cmd, double_cmd_len)) - { - /* Skip over just one of the - cmd. In the end there will be just one left. */ + while (newline - line_start > double_cmd_len && + !memcmp(line_start, double_cmd, double_cmd_len)) { + // Skip over just one of the - cmd. In the end there will be just one left. line_start += strlen("- cmd: "); } - /* Hackish: fish 1.x rewriting a fish 2.0 history file can produce commands like "when: 123456". Ignore those. */ + // Hackish: fish 1.x rewriting a fish 2.0 history file can produce commands like "when: + // 123456". Ignore those. const char *cmd_when = "- cmd: when:"; const size_t cmd_when_len = strlen(cmd_when); - if (newline - line_start >= cmd_when_len && ! memcmp(line_start, cmd_when, cmd_when_len)) + if (newline - line_start >= cmd_when_len && !memcmp(line_start, cmd_when, cmd_when_len)) 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) { + // 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; - /* 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) - { - /* Hackish fast way to skip items created after our timestamp. This is the mechanism by which we avoid "seeing" commands from other sessions that started after we started. We try hard to ensure that our items are sorted by their timestamps, so in theory we could just break, but I don't think that works well if (for example) the clock changes. So we'll read all subsequent items. - */ - const char * const end = begin + mmap_length; - - /* Walk over lines that we think are interior. These lines are not null terminated, but are guaranteed to contain a newline. */ + // Walk over lines that we think are interior. These lines are not null terminated, but + // are guaranteed to contain a newline. bool has_timestamp = false; time_t timestamp = 0; 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] != ' ') break; - /* If the first character is not a space, it's not an interior line, so we're done */ - if (interior_line[0] != ' ') - break; - - /* Hackish optimization: since we just stepped over some interior line, update the cursor so we don't have to look at these lines next time */ + // Hackish optimization: since we just stepped over some interior line, update the + // cursor so we don't have to look at these lines next time. cursor = interior_line - begin; - /* Try parsing a timestamp from this line. If we succeed, the loop will break. */ + // Try parsing a timestamp from this line. If we succeed, the loop will break. has_timestamp = parse_timestamp(interior_line, ×tamp); } - /* Skip this item if the timestamp is past our cutoff. */ - if (has_timestamp && timestamp > cutoff_timestamp) - { + // Skip this item if the timestamp is past our cutoff. + if (has_timestamp && timestamp > cutoff_timestamp) { continue; } } - /* We made it through the gauntlet. */ + // We made it through the gauntlet. result = line_start - begin; break; } @@ -474,13 +415,11 @@ static size_t offset_of_next_item_fish_2_0(const char *begin, size_t mmap_length return result; } - -// Same as offset_of_next_item_fish_2_0, but for fish 1.x (pre fishfish) +// 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) -{ - if (mmap_length == 0 || *inout_cursor >= mmap_length) - return (size_t)(-1); +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; @@ -490,32 +429,23 @@ static size_t offset_of_next_item_fish_1_x(const char *begin, size_t mmap_length 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++) { + if (do_push) { ignore_newline = (*pos == '#'); do_push = false; } - switch (*pos) - { - case '\\': - { + switch (*pos) { + case '\\': { pos++; break; } - - case '\n': - { - if (ignore_newline) - { + 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 */ + } else { + // Note: pos will be left pointing just after this newline, because of the ++ in + // the loop. all_done = true; } break; @@ -526,151 +456,140 @@ static size_t offset_of_next_item_fish_1_x(const char *begin, size_t mmap_length 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) -{ +// 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) { 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); + 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); + } + 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: + case history_type_unknown: { // Oh well result = (size_t)(-1); break; + } } return result; } -history_t & history_collection_t::alloc(const wcstring &name) -{ - /* Note that histories are currently never deleted, so we can return a reference to them without using something like shared_ptr */ +history_t &history_collection_t::alloc(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(m_lock); - history_t *& current = m_histories[name]; - if (current == NULL) - current = new history_t(name); + history_t *¤t = m_histories[name]; + if (current == NULL) current = new history_t(name); return *current; } -history_t & history_t::history_with_name(const wcstring &name) -{ - return histories.alloc(name); -} +history_t &history_t::history_with_name(const wcstring &name) { return histories.alloc(name); } -history_t::history_t(const wcstring &pname) : - name(pname), - first_unwritten_new_item_index(0), - has_pending_item(false), - disable_automatic_save_counter(0), - mmap_start(NULL), - mmap_length(0), - mmap_type(history_file_type_t(-1)), - mmap_file_id(kInvalidFileID), - boundary_timestamp(time(NULL)), - countdown_to_vacuum(-1), - loaded_old(false), - chaos_mode(false) -{ +history_t::history_t(const wcstring &pname) + : name(pname), + first_unwritten_new_item_index(0), + has_pending_item(false), + disable_automatic_save_counter(0), + mmap_start(NULL), + mmap_length(0), + mmap_type(history_file_type_t(-1)), + mmap_file_id(kInvalidFileID), + boundary_timestamp(time(NULL)), + countdown_to_vacuum(-1), + loaded_old(false), + chaos_mode(false) { pthread_mutex_init(&lock, NULL); } -history_t::~history_t() -{ - pthread_mutex_destroy(&lock); -} +history_t::~history_t() { pthread_mutex_destroy(&lock); } -void history_t::add(const history_item_t &item, bool pending) -{ +void history_t::add(const history_item_t &item, bool pending) { scoped_lock locker(lock); - /* Try merging with the last item */ - if (! new_items.empty() && new_items.back().merge(item)) - { - /* We merged, so we don't have to add anything. Maybe this item was pending, but it just got merged with an item that is not pending, so pending just becomes false. */ + // Try merging with the last item. + if (!new_items.empty() && new_items.back().merge(item)) { + // We merged, so we don't have to add anything. Maybe this item was pending, but it just got + // merged with an item that is not pending, so pending just becomes false. this->has_pending_item = false; - } - else - { - /* We have to add a new item */ + } else { + // We have to add a new item. new_items.push_back(item); this->has_pending_item = pending; save_internal_unless_disabled(); } } -void history_t::save_internal_unless_disabled() -{ - /* This must be called while locked */ +void history_t::save_internal_unless_disabled() { + // This must be called while locked. ASSERT_IS_LOCKED(lock); - /* Respect disable_automatic_save_counter */ - if (disable_automatic_save_counter > 0) - { + // Respect disable_automatic_save_counter. + if (disable_automatic_save_counter > 0) { return; } - /* We may or may not vacuum. We try to vacuum every kVacuumFrequency items, but start the countdown at a random number so that even if the user never runs more than 25 commands, we'll eventually vacuum. If countdown_to_vacuum is -1, it means we haven't yet picked a value for the counter. */ + // We may or may not vacuum. We try to vacuum every kVacuumFrequency items, but start the + // countdown at a random number so that even if the user never runs more than 25 commands, we'll + // eventually vacuum. If countdown_to_vacuum is -1, it means we haven't yet picked a value for + // the counter. const int kVacuumFrequency = 25; - if (countdown_to_vacuum < 0) - { + if (countdown_to_vacuum < 0) { static unsigned int seed = (unsigned int)time(NULL); - /* Generate a number in the range [0, kVacuumFrequency) */ + // Generate a number in the range [0, kVacuumFrequency). countdown_to_vacuum = rand_r(&seed) / (RAND_MAX / kVacuumFrequency + 1); } - /* Determine if we're going to vacuum */ + // Determine if we're going to vacuum. bool vacuum = false; - if (countdown_to_vacuum == 0) - { + if (countdown_to_vacuum == 0) { countdown_to_vacuum = kVacuumFrequency; vacuum = true; } - /* This might be a good candidate for moving to a background thread */ + // This might be a good candidate for moving to a background thread. time_profiler_t profiler(vacuum ? "save_internal vacuum" : "save_internal no vacuum"); this->save_internal(vacuum); - /* Update our countdown */ + // Update our countdown. assert(countdown_to_vacuum > 0); countdown_to_vacuum--; } -void history_t::add(const wcstring &str, history_identifier_t ident, bool pending) -{ +void history_t::add(const wcstring &str, history_identifier_t ident, bool pending) { time_t when = time(NULL); - /* Big hack: do not allow timestamps equal to our boundary date. This is because we include items whose timestamps are equal to our boundary when reading old history, so we can catch "just closed" items. But this means that we may interpret our own items, that we just wrote, as old items, if we wrote them in the same second as our birthdate. - */ - if (when == this->boundary_timestamp) - { + // Big hack: do not allow timestamps equal to our boundary date. This is because we include + // items whose timestamps are equal to our boundary when reading old history, so we can catch + // "just closed" items. But this means that we may interpret our own items, that we just wrote, + // as old items, if we wrote them in the same second as our birthdate. + if (when == this->boundary_timestamp) { when++; } this->add(history_item_t(str, when, ident), pending); } -void history_t::remove(const wcstring &str) -{ - /* Add to our list of deleted items */ +void history_t::remove(const wcstring &str) { + // Add to our list of deleted items. deleted_items.insert(str); - /* Remove from our list of new items */ + // Remove from our list of new items. size_t idx = new_items.size(); - while (idx--) - { - if (new_items.at(idx).str() == str) - { + while (idx--) { + if (new_items.at(idx).str() == str) { new_items.erase(new_items.begin() + idx); - /* If this index is before our first_unwritten_new_item_index, then subtract one from that index so it stays pointing at the same item. If it is equal to or larger, then we have not yet writen this item, so we don't have to adjust the index. */ - if (idx < first_unwritten_new_item_index) - { + // If this index is before our first_unwritten_new_item_index, then subtract one from + // that index so it stays pointing at the same item. If it is equal to or larger, then + // we have not yet writen this item, so we don't have to adjust the index. + if (idx < first_unwritten_new_item_index) { first_unwritten_new_item_index--; } } @@ -678,157 +597,139 @@ void history_t::remove(const wcstring &str) assert(first_unwritten_new_item_index <= new_items.size()); } -void history_t::set_valid_file_paths(const wcstring_list_t &valid_file_paths, history_identifier_t ident) -{ - /* 0 identifier is used to mean "not necessary" */ - if (ident == 0) - { +void history_t::set_valid_file_paths(const wcstring_list_t &valid_file_paths, + history_identifier_t ident) { + // 0 identifier is used to mean "not necessary". + if (ident == 0) { return; } scoped_lock locker(lock); - /* Look for an item with the given identifier. It is likely to be at the end of new_items */ - for (history_item_list_t::reverse_iterator iter = new_items.rbegin(); iter != new_items.rend(); ++iter) - { - if (iter->identifier == ident) - { - /* Found it */ + // Look for an item with the given identifier. It is likely to be at the end of new_items. + for (history_item_list_t::reverse_iterator iter = new_items.rbegin(); iter != new_items.rend(); + ++iter) { + if (iter->identifier == ident) { // found it iter->required_paths = valid_file_paths; break; } } } -void history_t::get_string_representation(wcstring *result, const wcstring &separator) -{ +void history_t::get_string_representation(wcstring *result, const wcstring &separator) { scoped_lock locker(lock); bool first = true; std::set seen; - /* If we have a pending item, we skip the first encountered (i.e. last) new item */ + // If we have a pending item, we skip the first encountered (i.e. last) new item. bool next_is_pending = this->has_pending_item; - /* Append new items. Note that in principle we could use const_reverse_iterator, but we do not because reverse_iterator is not convertible to const_reverse_iterator ( http://github.com/fish-shell/fish-shell/issues/431 ) */ - for (history_item_list_t::reverse_iterator iter=new_items.rbegin(); iter < new_items.rend(); ++iter) - { - /* Skip a pending item if we have one */ - if (next_is_pending) - { + // Append new items. Note that in principle we could use const_reverse_iterator, but we do not + // because reverse_iterator is not convertible to const_reverse_iterator. See + // http://github.com/fish-shell/fish-shell/issues/431. + for (history_item_list_t::reverse_iterator iter = new_items.rbegin(); iter < new_items.rend(); + ++iter) { + // Skip a pending item if we have one. + if (next_is_pending) { next_is_pending = false; continue; } - /* Skip duplicates */ - if (! seen.insert(iter->str()).second) - continue; + // Skip duplicates. + if (!seen.insert(iter->str()).second) continue; - if (! first) - result->append(separator); + if (!first) result->append(separator); result->append(iter->str()); first = false; } - /* Append old items */ + // Append old items. load_old_if_needed(); - for (std::deque::reverse_iterator iter = old_item_offsets.rbegin(); iter != old_item_offsets.rend(); ++iter) - { + for (std::deque::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); + const history_item_t item = + history_t::decode_item(mmap_start + offset, mmap_length - offset, mmap_type); - /* Skip duplicates */ - if (! seen.insert(item.str()).second) - continue; + // Skip duplicates. + if (!seen.insert(item.str()).second) continue; - if (! first) - result->append(separator); + if (!first) result->append(separator); result->append(item.str()); first = false; } } -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 */ + // 0 is considered an invalid index. assert(idx > 0); idx--; - /* Determine how many "resolved" (non-pending) items we have. We can have at most one pending item, and it's always the last one. */ + // Determine how many "resolved" (non-pending) items we have. We can have at most one pending + // item, and it's always the last one. size_t resolved_new_item_count = new_items.size(); - if (this->has_pending_item && resolved_new_item_count > 0) - { + if (this->has_pending_item && resolved_new_item_count > 0) { resolved_new_item_count -= 1; } - /* idx=0 corresponds to the last resolved item */ - if (idx < resolved_new_item_count) - { + // idx == 0 corresponds to the last resolved item. + if (idx < resolved_new_item_count) { return new_items.at(resolved_new_item_count - idx - 1); } - /* Now look in our old items */ + // Now look in our old items. idx -= resolved_new_item_count; load_old_if_needed(); size_t old_item_count = old_item_offsets.size(); - if (idx < old_item_count) - { - /* idx=0 corresponds to last item in old_item_offsets */ + 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); } - /* Index past the valid range, so return an empty history item */ + // Index past the valid range, so return an empty history item. return history_item_t(wcstring(), 0); } -/* 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) -{ - /* Locate the newline */ +// 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) { + // Locate the newline. assert(cursor <= len); const char *start = base + cursor; const char *newline = (char *)memchr(start, '\n', len - cursor); - if (newline != NULL) - { - /* We found a newline. */ + if (newline != NULL) { // we found a newline result.assign(start, newline - start); - - /* Return the amount to advance the cursor; skip over the newline */ + // Return the amount to advance the cursor; skip over the newline. return newline - start + 1; - } - else - { - /* We ran off the end */ + } else { + // We ran off the end. result.clear(); return len - cursor; } } -/* Trims leading spaces in the given string, returning how many there were */ -static size_t trim_leading_spaces(std::string &str) -{ +// Trims leading spaces in the given string, returning how many there were. +static size_t trim_leading_spaces(std::string &str) { size_t i = 0, max = str.size(); - while (i < max && str[i] == ' ') - i++; + while (i < max && str[i] == ' ') i++; str.erase(0, i); return i; } -static bool extract_prefix_and_unescape_yaml(std::string *key, std::string *value, const std::string &line) -{ +static bool extract_prefix_and_unescape_yaml(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->assign(line, 0, where); - // skip a space after the : if necessary + // Skip a space after the : if necessary. size_t val_start = where + 1; - if (val_start < line.size() && line.at(val_start) == ' ') - val_start++; + if (val_start < line.size() && line.at(val_start) == ' ') val_start++; value->assign(line, val_start, line.size() - val_start); unescape_yaml(key); @@ -837,9 +738,8 @@ static bool extract_prefix_and_unescape_yaml(std::string *key, std::string *valu return where != std::string::npos; } -/* 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) -{ +// 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) { wcstring cmd; time_t when = 0; path_list_t paths; @@ -847,60 +747,48 @@ history_item_t history_t::decode_item_fish_2_0(const char *base, size_t len) size_t indent = 0, cursor = 0; std::string key, value, line; - /* Read the "- cmd:" line */ + // Read the "- cmd:" line. size_t advance = read_line(base, cursor, len, line); trim_leading_spaces(line); - if (! extract_prefix_and_unescape_yaml(&key, &value, line) || key != "- cmd") - { + if (!extract_prefix_and_unescape_yaml(&key, &value, line) || key != "- cmd") { goto done; } cursor += advance; cmd = str2wcstring(value); - /* Read the remaining lines */ - for (;;) - { - /* Read a line */ + // Read the remaining lines. + for (;;) { size_t advance = read_line(base, cursor, len, line); - /* Count and trim leading spaces */ size_t this_indent = trim_leading_spaces(line); - if (indent == 0) - indent = this_indent; + if (indent == 0) indent = this_indent; - if (this_indent == 0 || indent != this_indent) - break; + if (this_indent == 0 || indent != this_indent) break; - if (! extract_prefix_and_unescape_yaml(&key, &value, line)) - break; + if (!extract_prefix_and_unescape_yaml(&key, &value, line)) break; - /* We are definitely going to consume this line */ + // We are definitely going to consume this line. cursor += advance; - if (key == "when") - { - /* Parse an int from the timestamp. Should this fail, strtol returns 0; that's acceptable. */ + if (key == "when") { + // Parse an int from the timestamp. Should this fail, strtol returns 0; that's + // acceptable. char *end = NULL; long tmp = strtol(value.c_str(), &end, 0); when = tmp; - } - else if (key == "paths") - { - /* Read lines starting with " - " until we can't read any more */ - for (;;) - { + } else if (key == "paths") { + // Read lines starting with " - " until we can't read any more. + for (;;) { size_t advance = read_line(base, cursor, len, line); - if (trim_leading_spaces(line) <= indent) - break; + if (trim_leading_spaces(line) <= indent) break; - if (strncmp(line.c_str(), "- ", 2)) - break; + if (strncmp(line.c_str(), "- ", 2)) break; - /* We're going to consume this line */ + // We're going to consume this line. cursor += advance; - /* Skip the leading dash-space and then store this path it */ + // Skip the leading dash-space and then store this path it. line.erase(0, 2); unescape_yaml(&line); paths.push_back(str2wcstring(line)); @@ -913,10 +801,8 @@ done: return result; } -history_item_t history_t::decode_item(const char *base, size_t len, history_file_type_t type) -{ - switch (type) - { +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: @@ -926,35 +812,24 @@ 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) -{ +// 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) { wcstring out; - for (const wchar_t *in = in_str.c_str(); *in; in++) - { - if (*in == L'\\') - { - if (*(in+1)!= L'\n') - { + for (const wchar_t *in = in_str.c_str(); *in; in++) { + if (*in == L'\\') { + if (*(in + 1) != L'\n') { out.push_back(*in); } - } - else - { + } else { out.push_back(*in); } } 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) -{ - +// 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) { const char *end = begin + length; const char *pos = begin; wcstring out; @@ -963,74 +838,56 @@ 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; size_t res; mbstate_t state = {}; - if (MB_CUR_MAX == 1) // single-byte locale - { + if (MB_CUR_MAX == 1) { // single-byte locale c = (unsigned char)*pos; res = 1; - } - else - { + } else { 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 (timestamp_mode) - { + if (c == L'\n') { + if (timestamp_mode) { const wchar_t *time_string = out.c_str(); - while (*time_string && !iswdigit(*time_string)) - time_string++; - errno=0; + 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); - if (tm && !errno && !*end) - { + if (tm && !errno && !*end) { timestamp = tm; } - } out.clear(); timestamp_mode = false; continue; } - if (!was_backslash) - break; + if (!was_backslash) break; } - if (first_char) - { - if (c == L'#') - timestamp_mode = true; + if (first_char) { + if (c == L'#') timestamp_mode = true; } first_char = false; @@ -1038,81 +895,71 @@ 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); - } out = history_unescape_newlines_fish_1_x(out); return history_item_t(out, timestamp); } - -/* 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) -{ +// 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) { history_file_type_t result = history_type_unknown; - if (len > 0) - { - /* Old fish started with a # */ - if (data[0] == '#') - { + if (len > 0) { // old fish started with a # + if (data[0] == '#') { result = history_type_fish_1_x; - } - else - { - /* Assume new fish */ + } else { // assume new fish result = history_type_fish_2_0; } } return result; } -void history_t::populate_from_mmap(void) -{ +void history_t::populate_from_mmap(void) { mmap_type = infer_file_type(mmap_start, mmap_length); size_t cursor = 0; - for (;;) - { - size_t offset = offset_of_next_item(mmap_start, mmap_length, mmap_type, &cursor, boundary_timestamp); - // If we get back -1, we're done - if (offset == (size_t)(-1)) - break; + for (;;) { + size_t offset = + offset_of_next_item(mmap_start, mmap_length, mmap_type, &cursor, boundary_timestamp); + // If we get back -1, we're done. + if (offset == (size_t)(-1)) break; - // Remember this item + // Remember this item. old_item_offsets.push_back(offset); } } -/* Do a private, read-only map of the entirety of a history file with the given name. Returns true if successful. Returns the mapped memory region by reference. */ -bool history_t::map_file(const wcstring &name, const char **out_map_start, size_t *out_map_len, file_id_t *file_id) -{ +// Do a private, read-only map of the entirety of a history file with the given name. Returns true +// if successful. Returns the mapped memory region by reference. +bool history_t::map_file(const wcstring &name, const char **out_map_start, size_t *out_map_len, + file_id_t *file_id) { bool result = false; wcstring filename = history_filename(name, L""); - if (! filename.empty()) - { + if (!filename.empty()) { int fd = wopen_cloexec(filename, O_RDONLY); - if (fd >= 0) - { + if (fd >= 0) { + // Get the file ID if requested. + if (file_id != NULL) *file_id = file_id_for_fd(fd); - /* Get the file ID if requested */ - if (file_id != NULL) - *file_id = file_id_for_fd(fd); - - /* Take a read lock to guard against someone else appending. This is released when the file is closed (below). We will read the file after releasing the lock, but that's not a problem, because we never modify already written data. In short, the purpose of this lock is to ensure we don't see the file size change mid-update. - - We may fail to lock (e.g. on lockless NFS - see https://github.com/fish-shell/fish-shell/issues/685 ). In that case, we proceed as if it did not fail. The risk is that we may get an incomplete history item; this is unlikely because we only treat an item as valid if it has a terminating newline. - - Simulate a failing lock in chaos_mode - */ - if (! chaos_mode) history_file_lock(fd, F_RDLCK); + // Take a read lock to guard against someone else appending. This is released when the + // file is closed (below). We will read the file after releasing the lock, but that's + // not a problem, because we never modify already written data. In short, the purpose of + // this lock is to ensure we don't see the file size change mid-update. + // + // We may fail to lock (e.g. on lockless NFS - see + // https://github.com/fish-shell/fish-shell/issues/685 ). In that case, we proceed as + // if it did not fail. The risk is that we may get an incomplete history item; this + // is unlikely because we only treat an item as valid if it has a terminating + // newline. + // + // Simulate a failing lock in chaos_mode. + if (!chaos_mode) history_file_lock(fd, F_RDLCK); off_t len = lseek(fd, 0, SEEK_END); - if (len != (off_t)-1) - { + if (len != (off_t)-1) { size_t mmap_length = (size_t)len; - if (lseek(fd, 0, SEEK_SET) == 0) - { + 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) - { + 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; @@ -1125,82 +972,69 @@ bool history_t::map_file(const wcstring &name, const char **out_map_start, size_ return result; } -bool history_t::load_old_if_needed(void) -{ +bool history_t::load_old_if_needed(void) { if (loaded_old) return true; loaded_old = true; - // PCA not sure why signals were blocked here - //signal_block(); + // signal_block(); bool ok = false; - if (map_file(name, &mmap_start, &mmap_length, &mmap_file_id)) - { - // Here we've mapped the file + if (map_file(name, &mmap_start, &mmap_length, &mmap_file_id)) { + // 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() -{ - /* Pop the top index (if more than one) and return if we have any left */ - if (prev_matches.size() > 1) - { +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) { prev_matches.pop_back(); return true; } return false; } -bool history_search_t::go_backwards() -{ - /* Backwards means increasing our index */ +bool history_search_t::go_backwards() { + // Backwards means increasing our index. const size_t max_idx = (size_t)(-1); size_t idx = 0; - if (! prev_matches.empty()) - idx = prev_matches.back().first; + if (!prev_matches.empty()) idx = prev_matches.back().first; - if (idx == max_idx) - return false; + if (idx == max_idx) return false; const bool main_thread = is_main_thread(); - while (++idx < max_idx) - { - if (main_thread ? reader_interrupted() : reader_thread_job_is_stale()) - { + while (++idx < max_idx) { + if (main_thread ? reader_interrupted() : reader_thread_job_is_stale()) { return false; } const history_item_t item = history->item_at_index(idx); - /* We're done if it's empty or we cancelled */ - if (item.empty()) - { + // We're done if it's empty or we cancelled. + if (item.empty()) { return false; } - /* Look for a term that matches and that we haven't seen before */ + // 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; } @@ -1208,110 +1042,86 @@ bool history_search_t::go_backwards() return false; } -/** Goes to the end (forwards) */ -void history_search_t::go_to_end(void) -{ - prev_matches.clear(); -} +// Goes to the end (forwards). +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 -{ - return prev_matches.empty(); -} +// Returns if we are at the end, which is where we start. +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) -{ - /* Just go backwards as far as we can */ +// Goes to the beginning (backwards). +void history_search_t::go_to_beginning(void) { + // Go backwards as far as we can. while (go_backwards()) - ; + ; // noop } - -history_item_t history_search_t::current_item() const -{ - assert(! prev_matches.empty()); +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) - { - if (iter->second.str() == match) - return true; +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; } return false; } -static void replace_all(std::string *str, const char *needle, const char *replacement) -{ +static void replace_all(std::string *str, const char *needle, const char *replacement) { 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) -{ - replace_all(str, "\\", "\\\\"); //replace one backslash with two - replace_all(str, "\n", "\\n"); //replace newline with backslash + literal n +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 } -/* This function is called frequently, so it ought to be fast. */ -static void unescape_yaml(std::string *str) -{ +// This function is called frequently, so it ought to be fast. +static void unescape_yaml(std::string *str) { size_t cursor = 0, size = str->size(); - while (cursor < size) - { + while (cursor < size) { // Operate on a const version of str, to avoid needless COWs that at() does. const std::string &const_str = *str; - // Look for a backslash + // Look for a backslash. size_t backslash = const_str.find('\\', cursor); - if (backslash == std::string::npos || backslash + 1 >= size) - { - // Either not found, or found as the last character + if (backslash == std::string::npos || backslash + 1 >= size) { + // Either not found, or found as the last character. break; - } - else - { - // Backslash found. Maybe we'll do something about it. Be sure to invoke the const version of at(). + } else { + // Backslash found. Maybe we'll do something about it. Be sure to invoke the const + // version of at(). char escaped_char = const_str.at(backslash + 1); - if (escaped_char == '\\') - { + if (escaped_char == '\\') { // Two backslashes in a row. Delete the second one. str->erase(backslash + 1, 1); size--; - } - else if (escaped_char == 'n') - { + } else if (escaped_char == 'n') { // Backslash + n. Replace with a newline. str->replace(backslash, 2, "\n"); size--; } - // The character at index backslash has now been made whole; start at the next character + // The character at index backslash has now been made whole; start at the next + // character. cursor = backslash + 1; } } } -static wcstring history_filename(const wcstring &name, const wcstring &suffix) -{ +static wcstring history_filename(const wcstring &name, const wcstring &suffix) { wcstring path; - if (! path_get_data(path)) - return L""; + if (!path_get_data(path)) return L""; wcstring result = path; result.append(L"/"); @@ -1321,12 +1131,10 @@ static wcstring history_filename(const wcstring &name, const wcstring &suffix) return result; } -void history_t::clear_file_state() -{ +void history_t::clear_file_state() { ASSERT_IS_LOCKED(lock); - /* Erase everything we know about our file */ - if (mmap_start != NULL && mmap_start != MAP_FAILED) - { + // Erase everything we know about our file. + if (mmap_start != NULL && mmap_start != MAP_FAILED) { munmap((void *)mmap_start, mmap_length); } mmap_start = NULL; @@ -1335,110 +1143,103 @@ void history_t::clear_file_state() old_item_offsets.clear(); } -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. */ +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) - { - // This item was not inserted because it was already in the set, so delete the item at this index + 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); - if (idx < first_unwritten_new_item_index) - { - /* Decrement first_unwritten_new_item_index if we are deleting a previously written item */ + if (idx < first_unwritten_new_item_index) { + // Decrement first_unwritten_new_item_index if we are deleting a previously written + // item. first_unwritten_new_item_index--; } } } } -bool history_t::save_internal_via_rewrite() -{ - /* This must be called while locked */ +bool history_t::save_internal_via_rewrite() { + // This must be called while locked. ASSERT_IS_LOCKED(lock); - bool ok = false; wcstring tmp_name_template = history_filename(name, L".XXXXXX"); - if (! tmp_name_template.empty()) - { - /* Make an LRU cache to save only the last N elements */ + if (!tmp_name_template.empty()) { + // Make an LRU cache to save only the last N elements. history_lru_cache_t lru(HISTORY_SAVE_MAX); - /* Insert old items in, from old to new. Merge them with our new items, inserting items with earlier timestamps first. */ + // Insert old items in, from old to new. Merge them with our new items, inserting items with + // earlier timestamps first. history_item_list_t::const_iterator new_item_iter = new_items.begin(); - /* Map in existing items (which may have changed out from underneath us, so don't trust our old mmap'd data) */ + // 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, NULL)) - { - const history_file_type_t local_mmap_type = infer_file_type(local_mmap_start, local_mmap_size); + if (map_file(name, &local_mmap_start, &local_mmap_size, NULL)) { + const history_file_type_t local_mmap_type = + infer_file_type(local_mmap_start, local_mmap_size); size_t cursor = 0; - 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)) - break; + 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)) break; - /* Try decoding an old item */ - const history_item_t old_item = history_t::decode_item(local_mmap_start + offset, local_mmap_size - offset, local_mmap_type); - if (old_item.empty() || deleted_items.count(old_item.str()) > 0) - { -// debug(0, L"Item is deleted : %s\n", old_item.str().c_str()); + // Try decoding an old item. + const history_item_t old_item = history_t::decode_item( + local_mmap_start + offset, local_mmap_size - offset, local_mmap_type); + if (old_item.empty() || deleted_items.count(old_item.str()) > 0) { + // debug(0, L"Item is deleted : %s\n", + // old_item.str().c_str()); 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()) - { - /* This "new item" is in fact older. */ + // 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()) { + // This "new item" is in fact older. lru.add_item(*new_item_iter); - } - else - { - /* The new item is not older. */ + } else { + // The new item is not older. break; } } - /* Now add this old item */ + // Now add this old item. lru.add_item(old_item); } munmap((void *)local_mmap_start, local_mmap_size); } - /* Insert any remaining new items */ - for (; new_item_iter != new_items.end(); ++new_item_iter) - { + // Insert any remaining new items. + for (; new_item_iter != new_items.end(); ++new_item_iter) { lru.add_item(*new_item_iter); } signal_block(); - /* Try to create a temporary file, up to 10 times. We don't use mkstemps because we want to open it CLO_EXEC. This should almost always succeed on the first try. */ + // Try to create a temporary file, up to 10 times. We don't use mkstemps because we want to + // open it CLO_EXEC. This should almost always succeed on the first try. int out_fd = -1; wcstring tmp_name; - for (size_t attempt = 0; attempt < 10 && out_fd == -1; attempt++) - { + for (size_t attempt = 0; attempt < 10 && out_fd == -1; attempt++) { char *narrow_str = wcs2str(tmp_name_template.c_str()); #if HAVE_MKOSTEMP out_fd = mkostemp(narrow_str, O_CLOEXEC); - if (out_fd >= 0) - { + if (out_fd >= 0) { tmp_name = str2wcstring(narrow_str); } #else - if (narrow_str && mktemp(narrow_str)) - { - /* It was successfully templated; try opening it atomically */ + if (narrow_str && mktemp(narrow_str)) { + // It was successfully templated; try opening it atomically. tmp_name = str2wcstring(narrow_str); out_fd = wopen_cloexec(tmp_name, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0600); } @@ -1446,60 +1247,46 @@ bool history_t::save_internal_via_rewrite() free(narrow_str); } - if (out_fd >= 0) - { - /* Write them out */ + if (out_fd >= 0) { + // Write them out. bool errored = false; history_output_buffer_t buffer; - 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; append_yaml_to_buffer(node->key, node->timestamp, node->required_paths, &buffer); - if (buffer.output_size() >= HISTORY_OUTPUT_BUFFER_SIZE && ! buffer.flush_to_fd(out_fd)) - { + if (buffer.output_size() >= HISTORY_OUTPUT_BUFFER_SIZE && + !buffer.flush_to_fd(out_fd)) { errored = true; break; } } - if (! errored && buffer.flush_to_fd(out_fd)) - { + if (!errored && buffer.flush_to_fd(out_fd)) { ok = true; } - if (! ok) - { - /* - This message does not have high enough priority to - be shown by default. - */ + if (!ok) { + // This message does not have high enough priority to be shown by default. debug(2, L"Error when writing history file"); - } - else - { + } else { wcstring new_name = history_filename(name, wcstring()); - /* Ensure we maintain the ownership and permissions of the original (#2355). - * If the stat fails, we assume (hope) our default permissions are correct. This - * corresponds to e.g. someone running sudo -E as the very first command. If they - * did, it would be tricky to set the permissions correctly. (bash doesn't get this - * case right either). */ + // Ensure we maintain the ownership and permissions of the original (#2355). If the + // stat fails, we assume (hope) our default permissions are correct. This + // corresponds to e.g. someone running sudo -E as the very first command. If they + // did, it would be tricky to set the permissions correctly. (bash doesn't get this + // case right either). struct stat sbuf; - if (wstat(new_name, &sbuf) >= 0) - { - /* Success */ - if (fchown(out_fd, sbuf.st_uid, sbuf.st_gid) == -1) - { + if (wstat(new_name, &sbuf) >= 0) { // success + if (fchown(out_fd, sbuf.st_uid, sbuf.st_gid) == -1) { debug(2, L"Error %d when changing ownership of history file", errno); } - if (fchmod(out_fd, sbuf.st_mode) == -1) - { + if (fchmod(out_fd, sbuf.st_mode) == -1) { debug(2, L"Error %d when changing mode of history file", errno); } } - if (wrename(tmp_name, new_name) == -1) - { + if (wrename(tmp_name, new_name) == -1) { debug(2, L"Error %d when renaming history file", errno); } } @@ -1508,90 +1295,92 @@ bool history_t::save_internal_via_rewrite() signal_unblock(); - /* Make sure we clear all nodes, since this doesn't happen automatically */ + // Make sure we clear all nodes, since this doesn't happen automatically. lru.evict_all_nodes(); } - if (ok) - { - /* We've saved everything, so we have no more unsaved items */ + if (ok) { + // We've saved everything, so we have no more unsaved items. this->first_unwritten_new_item_index = new_items.size(); - /* We deleted our deleted items */ + // We deleted our deleted items. this->deleted_items.clear(); - /* Our history has been written to the file, so clear our state so we can re-reference the file. */ + // Our history has been written to the file, so clear our state so we can re-reference the + // file. this->clear_file_state(); } - return ok; } -bool history_t::save_internal_via_appending() -{ - /* This must be called while locked */ +bool history_t::save_internal_via_appending() { + // This must be called while locked. ASSERT_IS_LOCKED(lock); - /* No deleting allowed */ + // No deleting allowed. assert(deleted_items.empty()); bool ok = false; - /* If the file is different (someone vacuumed it) then we need to update our mmap */ + // If the file is different (someone vacuumed it) then we need to update our mmap. bool file_changed = false; - /* Get the path to the real history file */ + // Get the path to the real history file. wcstring history_path = history_filename(name, wcstring()); signal_block(); - /* Open the file */ + // Open the file. int out_fd = wopen_cloexec(history_path, O_WRONLY | O_APPEND); - if (out_fd >= 0) - { - /* Check to see if the file changed */ - if (file_id_for_fd(out_fd) != mmap_file_id) - file_changed = true; + if (out_fd >= 0) { + // Check to see if the file changed. + if (file_id_for_fd(out_fd) != mmap_file_id) file_changed = true; - /* Exclusive lock on the entire file. This is released when we close the file (below). This may fail on (e.g.) lockless NFS. If so, proceed as if it did not fail; the risk is that we may get interleaved history items, which is considered better than no history, or forcing everything through the slow copy-move mode. We try to minimize this possibility by writing with O_APPEND. + // Exclusive lock on the entire file. This is released when we close the file (below). This + // may fail on (e.g.) lockless NFS. If so, proceed as if it did not fail; the risk is that + // we may get interleaved history items, which is considered better than no history, or + // forcing everything through the slow copy-move mode. We try to minimize this possibility + // by writing with O_APPEND. + // + // Simulate a failing lock in chaos_mode + if (!chaos_mode) history_file_lock(out_fd, F_WRLCK); - Simulate a failing lock in chaos_mode - */ - if (! chaos_mode) history_file_lock(out_fd, F_WRLCK); - - /* We (hopefully successfully) took the exclusive lock. Append to the file. - Note that this is sketchy for a few reasons: - - Another shell may have appended its own items with a later timestamp, so our file may no longer be sorted by timestamp. - - Another shell may have appended the same items, so our file may now contain duplicates. - - We cannot modify any previous parts of our file, because other instances may be reading those portions. We can only append. - - Originally we always rewrote the file on saving, which avoided both of these problems. However, appending allows us to save history after every command, which is nice! - - Periodically we "clean up" the file by rewriting it, so that most of the time it doesn't have duplicates, although we don't yet sort by timestamp (the timestamp isn't really used for much anyways). - */ - - /* So far so good. Write all items at or after first_unwritten_new_item_index. Note that we write even a pending item - pending items are ignored by history within the command itself, but should still be written to the file. */ + // We (hopefully successfully) took the exclusive lock. Append to the file. + // Note that this is sketchy for a few reasons: + // - Another shell may have appended its own items with a later timestamp, so our file may + // no longer be sorted by timestamp. + // - Another shell may have appended the same items, so our file may now contain + // duplicates. + // + // We cannot modify any previous parts of our file, because other instances may be reading + // those portions. We can only append. + // + // Originally we always rewrote the file on saving, which avoided both of these problems. + // However, appending allows us to save history after every command, which is nice! + // + // Periodically we "clean up" the file by rewriting it, so that most of the time it doesn't + // have duplicates, although we don't yet sort by timestamp (the timestamp isn't really used + // for much anyways). + // So far so good. Write all items at or after first_unwritten_new_item_index. Note that we + // write even a pending item - pending items are ignored by history within the command + // itself, but should still be written to the file. bool errored = false; history_output_buffer_t buffer; - while (first_unwritten_new_item_index < new_items.size()) - { + while (first_unwritten_new_item_index < new_items.size()) { const history_item_t &item = new_items.at(first_unwritten_new_item_index); append_yaml_to_buffer(item.str(), item.timestamp(), item.get_required_paths(), &buffer); - if (buffer.output_size() >= HISTORY_OUTPUT_BUFFER_SIZE) - { - errored = ! buffer.flush_to_fd(out_fd); + if (buffer.output_size() >= HISTORY_OUTPUT_BUFFER_SIZE) { + errored = !buffer.flush_to_fd(out_fd); if (errored) break; } - /* We wrote this item, hooray */ + // We wrote this item, hooray. first_unwritten_new_item_index++; } - if (! errored && buffer.flush_to_fd(out_fd)) - { + if (!errored && buffer.flush_to_fd(out_fd)) { ok = true; } @@ -1600,145 +1389,116 @@ bool history_t::save_internal_via_appending() signal_unblock(); - /* If someone has replaced the file, forget our file state */ - if (file_changed) - { + // If someone has replaced the file, forget our file state. + if (file_changed) { this->clear_file_state(); } return ok; } -/** Save the specified mode to file; optionally also vacuums */ -void history_t::save_internal(bool vacuum) -{ - /* This must be called while locked */ +// Save the specified mode to file; optionally also vacuums. +void history_t::save_internal(bool vacuum) { ASSERT_IS_LOCKED(lock); - /* Nothing to do if there's no new items */ - if (first_unwritten_new_item_index >= new_items.size() && deleted_items.empty()) - return; + // Nothing to do if there's no new items. + if (first_unwritten_new_item_index >= new_items.size() && deleted_items.empty()) return; - /* Compact our new items so we don't have duplicates */ + // Compact our new items so we don't have duplicates. this->compact_new_items(); - /* Try saving. If we have items to delete, we have to rewrite the file. If we do not, we can append to it. */ + // Try saving. If we have items to delete, we have to rewrite the file. If we do not, we can + // append to it. bool ok = false; - if (! vacuum && deleted_items.empty()) - { - /* Try doing a fast append */ + if (!vacuum && deleted_items.empty()) { + // Try doing a fast append. ok = save_internal_via_appending(); } - if (! ok) - { - /* We did not or could not append; rewrite the file ("vacuum" it) */ + if (!ok) { + // We did not or could not append; rewrite the file ("vacuum" it). ok = this->save_internal_via_rewrite(); } } -void history_t::save(void) -{ +void history_t::save(void) { scoped_lock locker(lock); this->save_internal(false); } -void history_t::disable_automatic_saving() -{ +void history_t::disable_automatic_saving() { scoped_lock locker(lock); disable_automatic_save_counter++; - assert(disable_automatic_save_counter != 0); // overflow! + assert(disable_automatic_save_counter != 0); // overflow! } -void history_t::enable_automatic_saving() -{ +void history_t::enable_automatic_saving() { scoped_lock locker(lock); - assert(disable_automatic_save_counter > 0); //underflow + assert(disable_automatic_save_counter > 0); // underflow disable_automatic_save_counter--; save_internal_unless_disabled(); } - -void history_t::clear(void) -{ +void history_t::clear(void) { scoped_lock locker(lock); new_items.clear(); deleted_items.clear(); first_unwritten_new_item_index = 0; old_item_offsets.clear(); wcstring filename = history_filename(name, L""); - if (! filename.empty()) - wunlink(filename); + if (!filename.empty()) wunlink(filename); this->clear_file_state(); - } -bool history_t::is_empty(void) -{ +bool history_t::is_empty(void) { scoped_lock locker(lock); - /* If we have new items, we're not empty */ - if (! new_items.empty()) - return false; + // If we have new items, we're not empty. + if (!new_items.empty()) return false; bool empty = false; - if (loaded_old) - { - /* If we've loaded old items, see if we have any offsets */ + if (loaded_old) { + // If we've loaded old items, see if we have any offsets. empty = old_item_offsets.empty(); - } - else - { - /* If we have not loaded old items, don't actually load them (which may be expensive); just stat the file and see if it exists and is nonempty */ + } else { + // If we have not loaded old items, don't actually load them (which may be expensive); just + // stat the file and see if it exists and is nonempty. const wcstring where = history_filename(name, L""); struct stat buf = {}; - if (wstat(where, &buf) != 0) - { - /* Access failed, assume missing */ + if (wstat(where, &buf) != 0) { + // Access failed, assume missing. empty = true; - } - else - { - /* We're empty if the file is empty */ + } else { + // We're empty if the file is empty. empty = (buf.st_size == 0); } } return empty; } - -/* Populates from older location (in config path, rather than data path) - This is accomplished by clearing ourselves, and copying the contents of - the old history file to the new history file. The new contents will - automatically be re-mapped later. -*/ -void history_t::populate_from_config_path() -{ +// Populates from older location (in config path, rather than data path) This is accomplished by +// clearing ourselves, and copying the contents of the old history file to the new history file. +// The new contents will automatically be re-mapped later. +void history_t::populate_from_config_path() { wcstring old_file; if (path_get_config(old_file)) { old_file.append(L"/"); old_file.append(name); old_file.append(L"_history"); int src_fd = wopen_cloexec(old_file, O_RDONLY, 0); - if (src_fd != -1) - { + if (src_fd != -1) { wcstring new_file = history_filename(name, wcstring()); - /* clear must come after we've retrieved the new_file name, - and before we open destination file descriptor, - since it destroys the name and the file */ + // Clear must come after we've retrieved the new_file name, and before we open + // destination file descriptor, since it destroys the name and the file. this->clear(); int dst_fd = wopen_cloexec(new_file, O_WRONLY | O_CREAT, 0644); - char buf[BUFSIZ]; ssize_t size; while ((size = read(src_fd, buf, BUFSIZ)) > 0) { ssize_t written = write(dst_fd, buf, static_cast(size)); if (written < 0) { - /* - This message does not have high enough priority to - be shown by default. - */ + // This message does not have high enough priority to be shown by default. debug(2, L"Error when writing history file"); break; } @@ -1750,263 +1510,215 @@ void history_t::populate_from_config_path() } } +// Indicate whether we ought to import the bash history file into fish. +static bool should_import_bash_history_line(const std::string &line) { + if (line.empty()) return false; -/* Indicate whether we ought to import the bash history file into fish */ -static bool should_import_bash_history_line(const std::string &line) -{ - if (line.empty()) - return false; + // Very naive tests! Skip export; probably should skip others. + const char *const ignore_prefixes[] = {"export ", "#"}; - /* Very naive tests! Skip export; probably should skip others. */ - const char * const ignore_prefixes[] = - { - "export ", - "#" - }; - - for (size_t i=0; i < sizeof ignore_prefixes / sizeof *ignore_prefixes; i++) - { + 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; } } - /* Skip lines with backticks */ - if (line.find('`') != std::string::npos) - return false; + // Skip lines with backticks. + if (line.find('`') != std::string::npos) return false; return true; } -void history_t::populate_from_bash(FILE *stream) -{ - /* Bash's format is very simple: just lines with #s for comments. - Ignore a few commands that are bash-specific. This list ought to be expanded. - */ +void history_t::populate_from_bash(FILE *stream) { + // Bash's format is very simple: just lines with #s for comments. 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 - { + // Loop until we've read a line. + do { char buff[128]; - success = !! fgets(buff, sizeof buff, stream); - if (success) - { - /* Skip the newline */ + success = !!fgets(buff, sizeof buff, stream); + if (success) { + // Skip the newline. char *newline = strchr(buff, '\n'); if (newline) *newline = '\0'; has_newline = (newline != NULL); - /* Append what we've got */ + // 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)) - { + // Maybe add this line. + if (should_import_bash_history_line(line)) { this->add(str2wcstring(line)); } - if (line.empty()) - break; + if (line.empty()) break; } } -void history_t::incorporate_external_changes() -{ - /* To incorporate new items, we simply update our timestamp to now, so that items from previous instances get added. We then clear the file state so that we remap the file. Note that this is somehwhat expensive because we will be going back over old items. An optimization would be to preserve old_item_offsets so that they don't have to be recomputed. (However, then items *deleted* in other instances would not show up here). */ +void history_t::incorporate_external_changes() { + // To incorporate new items, we simply update our timestamp to now, so that items from previous + // instances get added. We then clear the file state so that we remap the file. Note that this + // is somehwhat expensive because we will be going back over old items. An optimization would be + // to preserve old_item_offsets so that they don't have to be recomputed. (However, then items + // *deleted* in other instances would not show up here). time_t new_timestamp = time(NULL); scoped_lock locker(lock); - /* If for some reason the clock went backwards, we don't want to start dropping items; therefore we only do work if time has progressed. This also makes multiple calls cheap. */ - if (new_timestamp > this->boundary_timestamp) - { + // If for some reason the clock went backwards, we don't want to start dropping items; therefore + // we only do work if time has progressed. This also makes multiple calls cheap. + if (new_timestamp > this->boundary_timestamp) { this->boundary_timestamp = new_timestamp; this->clear_file_state(); } } -void history_init() -{ -} +void history_init() {} - -void history_collection_t::save() -{ - /* Save all histories */ - for (std::map::iterator iter = m_histories.begin(); iter != m_histories.end(); ++iter) - { +void history_collection_t::save() { + // Save all histories. + for (std::map::iterator iter = m_histories.begin(); + iter != m_histories.end(); ++iter) { history_t *hist = iter->second; hist->save(); } } -void history_destroy() -{ - histories.save(); +void history_destroy() { histories.save(); } + +void history_sanity_check() { + // No sanity checking implemented yet... } - -void history_sanity_check() -{ - /* - 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)) - { - /* Push the original (possibly relative) path */ + 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 - { - /* Not a valid path */ + } else { + // Not a valid path. result = 0; - if (! test_all) - break; + if (!test_all) break; } } 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; } -file_detection_context_t::file_detection_context_t(history_t *hist, history_identifier_t ident) : - history(hist), - working_directory(env_get_pwd_slash()), - history_item_identifier(ident) -{ -} +file_detection_context_t::file_detection_context_t(history_t *hist, history_identifier_t ident) + : history(hist), working_directory(env_get_pwd_slash()), history_item_identifier(ident) {} -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) { ASSERT_IS_MAIN_THREAD(); - /* Now that file detection is done, update the history item with the valid file paths */ + // Now that file detection is done, update the history item with the valid file paths. ctx->history->set_valid_file_paths(ctx->valid_paths, ctx->history_item_identifier); - /* Allow saving again */ + // Allow saving again. ctx->history->enable_automatic_saving(); - /* Done with the context. */ + // Done with the context. delete ctx; } -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'-') - { +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; } return true; } -void history_t::add_pending_with_file_detection(const wcstring &str) -{ +void history_t::add_pending_with_file_detection(const wcstring &str) { ASSERT_IS_MAIN_THREAD(); path_list_t potential_paths; - /* Find all arguments that look like they could be file paths */ + // Find all arguments that look like they could be file paths. bool impending_exit = false; parse_node_tree_t tree; parse_tree_from_string(str, parse_flag_none, &tree, NULL); size_t count = tree.size(); - for (size_t i=0; i < count; i++) - { + for (size_t i = 0; i < count; i++) { const parse_node_t &node = tree.at(i); - if (! node.has_source()) - { + if (!node.has_source()) { continue; } - if (node.type == symbol_argument) - { + if (node.type == symbol_argument) { wcstring potential_path = node.get_source(str); bool unescaped = unescape_string_in_place(&potential_path, UNESCAPE_DEFAULT); - if (unescaped && string_could_be_path(potential_path)) - { + if (unescaped && string_could_be_path(potential_path)) { potential_paths.push_back(potential_path); } - } - else if (node.type == symbol_plain_statement) - { - /* Hack hack hack - if the command is likely to trigger an exit, then don't do background file detection, because we won't be able to write it to our history file before we exit. */ - if (tree.decoration_for_plain_statement(node) == parse_statement_decoration_exec) - { + } else if (node.type == symbol_plain_statement) { + // Hack hack hack - if the command is likely to trigger an exit, then don't do + // background file detection, because we won't be able to write it to our history file + // before we exit. + if (tree.decoration_for_plain_statement(node) == parse_statement_decoration_exec) { impending_exit = true; } wcstring command; tree.command_for_plain_statement(node, str, &command); unescape_string_in_place(&command, UNESCAPE_DEFAULT); - if (contains(command, L"exit", L"reboot")) - { + if (contains(command, L"exit", L"reboot")) { impending_exit = true; } } } - /* If we got a path, we'll perform file detection for autosuggestion hinting */ + // If we got a path, we'll perform file detection for autosuggestion hinting. history_identifier_t identifier = 0; - if (! potential_paths.empty() && ! impending_exit) - { - /* Grab the next identifier */ + if (!potential_paths.empty() && !impending_exit) { + // Grab the next identifier. static history_identifier_t sLastIdentifier = 0; identifier = ++sLastIdentifier; - /* Create a new detection context */ + // Create a new detection context. file_detection_context_t *context = new file_detection_context_t(this, identifier); context->potential_paths.swap(potential_paths); - /* Prevent saving until we're done, so we have time to get the paths */ + // Prevent saving until we're done, so we have time to get the paths. this->disable_automatic_saving(); - /* Kick it off. Even though we haven't added the item yet, it updates the item on the main thread, so we can't race */ + // Kick it off. Even though we haven't added the item yet, it updates the item on the main + // thread, so we can't race. iothread_perform(threaded_perform_file_detection, perform_file_detection_done, context); } - /* Actually add the item to the history. */ + // Actually add the item to the history. this->add(str, identifier, true /* pending */); - /* If we think we're about to exit, save immediately, regardless of any disabling. This may cause us to lose file hinting for some commands, but it beats losing history items */ - if (impending_exit) - { + // If we think we're about to exit, save immediately, regardless of any disabling. This may + // cause us to lose file hinting for some commands, but it beats losing history items. + if (impending_exit) { this->save(); } } -/* Very simple, just mark that we have no more pending items */ -void history_t::resolve_pending() -{ +// Very simple, just mark that we have no more pending items. +void history_t::resolve_pending() { scoped_lock locker(lock); this->has_pending_item = false; } diff --git a/src/history.h b/src/history.h index 58291f22a..b278c3ccf 100644 --- a/src/history.h +++ b/src/history.h @@ -1,377 +1,353 @@ -/** \file history.h - Prototypes for history functions, part of the user interface. -*/ - +// Prototypes for history functions, part of the user interface. #ifndef FISH_HISTORY_H #define FISH_HISTORY_H -#include "common.h" -#include "wutil.h" -#include -#include -#include -#include #include #include #include #include #include +#include +#include #include +#include +#include +#include "common.h" +#include "wutil.h" -/* fish supports multiple shells writing to history at once. Here is its strategy: - -1. All history files are append-only. Data, once written, is never modified. -2. A history file may be re-written ("vacuumed"). This involves reading in the file and writing a new one, while performing maintenance tasks: discarding items in an LRU fashion until we reach the desired maximum count, removing duplicates, and sorting them by timestamp (eventually, not implemented yet). The new file is atomically moved into place via rename(). -3. History files are mapped in via mmap(). Before the file is mapped, the file takes a fcntl read lock. The purpose of this lock is to avoid seeing a transient state where partial data has been written to the file. -4. History is appended to under a fcntl write lock. -5. The chaos_mode boolean can be set to true to do things like lower buffer sizes which can trigger race conditions. This is useful for testing. -*/ +// Fish supports multiple shells writing to history at once. Here is its strategy: +// +// 1. All history files are append-only. Data, once written, is never modified. +// +// 2. A history file may be re-written ("vacuumed"). This involves reading in the file and writing a +// new one, while performing maintenance tasks: discarding items in an LRU fashion until we reach +// the desired maximum count, removing duplicates, and sorting them by timestamp (eventually, not +// implemented yet). The new file is atomically moved into place via rename(). +// +// 3. History files are mapped in via mmap(). Before the file is mapped, the file takes a fcntl read +// lock. The purpose of this lock is to avoid seeing a transient state where partial data has been +// written to the file. +// +// 4. History is appended to under a fcntl write lock. +// +// 5. The chaos_mode boolean can be set to true to do things like lower buffer sizes which can +// trigger race conditions. This is useful for testing. typedef std::vector path_list_t; -enum history_search_type_t -{ - /** The history searches for strings containing the given string */ +enum history_search_type_t { + // The history searches for strings containing the given string. HISTORY_SEARCH_TYPE_CONTAINS, - - /** The history searches for strings starting with the given string */ + // The history searches for strings starting with the given string. HISTORY_SEARCH_TYPE_PREFIX }; typedef uint32_t history_identifier_t; -class history_item_t -{ +class history_item_t { friend class history_t; friend class history_tests_t; -private: + private: explicit history_item_t(const wcstring &str); explicit history_item_t(const wcstring &, time_t, history_identifier_t ident = 0); - /** Attempts to merge two compatible history items together */ + // Attempts to merge two compatible history items together. bool merge(const history_item_t &item); - /** The actual contents of the entry */ + // The actual contents of the entry. wcstring contents; - /** Original creation time for the entry */ + // Original creation time for the entry. time_t creation_timestamp; - /** Sometimes unique identifier used for hinting */ + // Sometimes unique identifier used for hinting. history_identifier_t identifier; - /** Paths that we require to be valid for this item to be autosuggested */ + // 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; - } + public: + const wcstring &str() const { return contents; } - bool empty() const - { - return contents.empty(); - } + bool empty() const { return contents.empty(); } - /* Whether our contents matches a search term. */ + // 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 - { - return contents == other.contents && - creation_timestamp == other.creation_timestamp && + bool operator==(const history_item_t &other) const { + return contents == other.contents && creation_timestamp == other.creation_timestamp && required_paths == other.required_paths; } }; typedef std::deque history_item_list_t; -/* The type of file that we mmap'd */ -enum history_file_type_t -{ - history_type_unknown, - history_type_fish_2_0, - history_type_fish_1_x -}; +// The type of file that we mmap'd. +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 */ - history_t(const history_t&); - history_t &operator=(const history_t&); - /** Privately add an item. If pending, the item will not be returned by history searches until a call to resolve_pending. */ + private: + // No copying. + history_t(const history_t &); + history_t &operator=(const history_t &); + + // Privately add an item. If pending, the item will not be returned by history searches until a + // call to resolve_pending. void add(const history_item_t &item, bool pending = false); - /** Lock for thread safety */ + // Lock for thread safety. pthread_mutex_t lock; - /** Internal function */ + // Internal function. void clear_file_state(); - /** The name of this list. Used for picking a suitable filename and for switching modes. */ + // The name of this list. Used for picking a suitable filename and for switching modes. const wcstring name; - /** New items. Note that these are NOT discarded on save. We need to keep these around so we can distinguish between items in our history and items in the history of other shells that were started after we were started. */ + // New items. Note that these are NOT discarded on save. We need to keep these around so we can + // distinguish between items in our history and items in the history of other shells that were + // started after we were started. history_item_list_t new_items; - /** The index of the first new item that we have not yet written. */ + // The index of the first new item that we have not yet written. size_t first_unwritten_new_item_index; - /** Whether we have a pending item. If so, the most recently added item is ignored by item_at_index. */ + // Whether we have a pending item. If so, the most recently added item is ignored by + // item_at_index. bool has_pending_item; - /** Whether we should disable saving to the file for a time */ + // Whether we should disable saving to the file for a time. uint32_t disable_automatic_save_counter; - /** Deleted item contents. */ + // Deleted item contents. std::set deleted_items; - /** The mmaped region for the history file */ + // The mmaped region for the history file. const char *mmap_start; - /** The size of the mmap'd region */ + // The size of the mmap'd region. size_t mmap_length; - /** The type of file we mmap'd */ + // The type of file we mmap'd. history_file_type_t mmap_type; - /** The file ID of the file we mmap'd */ + // The file ID of the file we mmap'd. file_id_t mmap_file_id; - /** The boundary timestamp distinguishes old items from new items. Items whose timestamps are <= the boundary are considered "old". Items whose timestemps are > the boundary are new, and are ignored by this instance (unless they came from this instance). The timestamp may be adjusted by incorporate_external_changes() */ + // The boundary timestamp distinguishes old items from new items. Items whose timestamps are <= + // the boundary are considered "old". Items whose timestemps are > the boundary are new, and are + // ignored by this instance (unless they came from this instance). The timestamp may be adjusted + // by incorporate_external_changes(). time_t boundary_timestamp; - /** How many items we add until the next vacuum. Initially a random value. */ + // How many items we add until the next vacuum. Initially a random value. int countdown_to_vacuum; - /** Figure out the offsets of our mmap data */ + // Figure out the offsets of our mmap data. void populate_from_mmap(void); - /** List of old items, as offsets into out mmap data */ + // List of old items, as offsets into out mmap data. std::deque old_item_offsets; - /** Whether we've loaded old items */ + // Whether we've loaded old items. bool loaded_old; - /** Loads old if necessary */ + // Loads old if necessary. bool load_old_if_needed(void); - /** Memory maps the history file if necessary */ + // Memory maps the history file if necessary. bool mmap_if_needed(void); - /** Deletes duplicates in new_items. */ + // Deletes duplicates in new_items. void compact_new_items(); - /** Saves history by rewriting the file */ + // Saves history by rewriting the file. bool save_internal_via_rewrite(); - /** Saves history by appending to the file */ + // Saves history by appending to the file. bool save_internal_via_appending(); - /** Saves history */ + // Saves history. void save_internal(bool vacuum); - /** Saves history, maybe */ + // Saves history unless doing so is disabled. void save_internal_unless_disabled(); - /* Do a private, read-only map of the entirety of a history file with the given name. Returns true if successful. Returns the mapped memory region by reference. */ - bool map_file(const wcstring &name, const char **out_map_start, size_t *out_map_len, file_id_t *file_id); + // Do a private, read-only map of the entirety of a history file with the given name. Returns + // true if successful. Returns the mapped memory region by reference. + bool map_file(const wcstring &name, const char **out_map_start, size_t *out_map_len, + file_id_t *file_id); - /** Whether we're in maximum chaos mode, useful for testing */ + // Whether we're in maximum chaos mode, useful for testing. bool chaos_mode; - /* Versioned decoding */ + // Versioned decoding. static history_item_t decode_item_fish_2_0(const char *base, size_t len); static history_item_t decode_item_fish_1_x(const char *base, size_t len); static history_item_t decode_item(const char *base, size_t len, history_file_type_t type); -public: - /** Constructor */ - explicit history_t(const wcstring &); + public: + explicit history_t(const wcstring &); // constructor + ~history_t(); // desctructor - /** Destructor */ - ~history_t(); + // Returns history with the given name, creating it if necessary. + static history_t &history_with_name(const wcstring &name); - /** Returns history with the given name, creating it if necessary */ - static history_t & history_with_name(const wcstring &name); - - /** Determines whether the history is empty. Unfortunately this cannot be const, since it may require populating the history. */ + // Determines whether the history is empty. Unfortunately this cannot be const, since it may + // require populating the history. bool is_empty(void); - /** Add a new history item to the end. If pending is set, the item will not be returned by item_at_index until a call to resolve_pending(). Pending items are tracked with an offset into the array of new items, so adding a non-pending item has the effect of resolving all pending items. */ + // Add a new history item to the end. If pending is set, the item will not be returned by + // item_at_index until a call to resolve_pending(). Pending items are tracked with an offset + // into the array of new items, so adding a non-pending item has the effect of resolving all + // pending items. void add(const wcstring &str, history_identifier_t ident = 0, bool pending = false); - /** Remove a history item */ + // Remove a history item. void remove(const wcstring &str); - /** Add a new pending history item to the end, and then begin file detection on the items to determine which arguments are paths */ + // Add a new pending history item to the end, and then begin file detection on the items to + // determine which arguments are paths void add_pending_with_file_detection(const wcstring &str); - /** Resolves any pending history items, so that they may be returned in history searches. */ + // Resolves any pending history items, so that they may be returned in history searches. void resolve_pending(); - /** Saves history */ + // Saves history. void save(); - /** Enable / disable automatic saving. Main thread only! */ + // Enable / disable automatic saving. Main thread only! void disable_automatic_saving(); void enable_automatic_saving(); - /** Irreversibly clears history */ + // Irreversibly clears history. void clear(); - /** Populates from older location ()in config path, rather than data path) */ + // Populates from older location ()in config path, rather than data path). void populate_from_config_path(); - /** Populates from a bash history file */ + // Populates from a bash history file. void populate_from_bash(FILE *f); - /** Incorporates the history of other shells into this history */ + // Incorporates the history of other shells into this history. void incorporate_external_changes(); - /* Gets all the history into a string with ARRAY_SEP_STR. This is intended for the $history environment variable. This may be long! */ + // Gets all the history into a string with ARRAY_SEP_STR. This is intended for the $history + // environment variable. This may be long! void get_string_representation(wcstring *result, const wcstring &separator); - /** Sets the valid file paths for the history item with the given identifier */ + // Sets the valid file paths for the history item with the given identifier. void set_valid_file_paths(const wcstring_list_t &valid_file_paths, history_identifier_t ident); - /** Return the specified history at the specified index. 0 is the index of the current commandline. (So the most recent item is at index 1.) */ + // Return the specified history at the specified index. 0 is the index of the current + // commandline. (So the most recent item is at index 1.) history_item_t item_at_index(size_t idx); }; -class history_search_t -{ +class history_search_t { + // The history in which we are searching. + history_t *history; - /** The history in which we are searching */ - history_t * history; - - /** Our type */ + // Our type. enum history_search_type_t search_type; - /** Our list of previous matches as index, value. The end is the current match. */ + // Our list of previous matches as index, value. The end is the current match. typedef std::pair prev_match_t; std::vector prev_matches; - /** Returns yes if a given term is in prev_matches. */ + // Returns yes if a given term is in prev_matches. bool match_already_made(const wcstring &match) const; - /** The search term */ + // The search term. wcstring term; - /** Additional strings to skip (sorted) */ + // Additional strings to skip (sorted). wcstring_list_t external_skips; bool should_skip_match(const wcstring &str) const; -public: + public: + // Gets the search term. + const wcstring &get_term() const { return term; } - /** Gets the search term */ - const wcstring &get_term() const - { - return term; - } - - /** Sets additional string matches to skip */ + // Sets additional string matches to skip. void skip_matches(const wcstring_list_t &skips); - /** Finds the next search term (forwards in time). Returns true if one was found. */ + // Finds the next search term (forwards in time). Returns true if one was found. bool go_forwards(void); - /** Finds the previous search result (backwards in time). Returns true if one was found. */ + // Finds the previous search result (backwards in time). Returns true if one was found. bool go_backwards(void); - /** Goes to the end (forwards) */ + // Goes to the end (forwards). void go_to_end(void); - /** Returns if we are at the end. We start out at the end. */ + // Returns if we are at the end. We start out at the end. bool is_at_end(void) const; - /** Goes to the beginning (backwards) */ + // Goes to the beginning (backwards). void go_to_beginning(void); - /** Returns the current search result item. asserts if there is no current item. */ + // Returns the current search result item. asserts if there is no current item. history_item_t current_item(void) const; - /** Returns the current search result item contents. asserts if there is no current item. */ + // Returns the current search result item contents. asserts if there is no current item. wcstring current_string(void) const; + // Constructor. + history_search_t(history_t &hist, const wcstring &str, + enum history_search_type_t type = HISTORY_SEARCH_TYPE_CONTAINS) + : history(&hist), search_type(type), term(str) {} - /** Constructor */ - history_search_t(history_t &hist, const wcstring &str, enum history_search_type_t type = HISTORY_SEARCH_TYPE_CONTAINS) : - history(&hist), - search_type(type), - term(str) - {} - - /* Default constructor */ - history_search_t() : - history(), - search_type(HISTORY_SEARCH_TYPE_CONTAINS), - term() - {} - + // Default constructor. + history_search_t() : history(), search_type(HISTORY_SEARCH_TYPE_CONTAINS), term() {} }; -/** - Init history library. The history file won't actually be loaded - until the first time a history search is performed. -*/ +// Init history library. The history file won't actually be loaded until the first time a history +// search is performed. void history_init(); -/** - Saves the new history to disc. -*/ +// Saves the new history to disk. void history_destroy(); -/** - Perform sanity checks -*/ +// Perform sanity checks. void history_sanity_check(); -/* A helper class for threaded detection of paths */ -struct file_detection_context_t -{ - /* Constructor */ +// A helper class for threaded detection of paths. +struct file_detection_context_t { + // Constructor. explicit file_detection_context_t(history_t *hist, history_identifier_t ident = 0); - /* Determine which of potential_paths are valid, and put them in valid_paths */ + // Determine which of potential_paths are valid, and put them in valid_paths. int perform_file_detection(); - /* The history associated with this context */ - history_t * const history; + // The history associated with this context. + history_t *const history; - /* The working directory at the time the command was issued */ + // The working directory at the time the command was issued. wcstring working_directory; - /* Paths to test */ + // Paths to test. path_list_t potential_paths; - /* Paths that were found to be valid */ + // Paths that were found to be valid. path_list_t valid_paths; - /* Identifier of the history item to which we are associated */ + // Identifier of the history item to which we are associated. const history_identifier_t history_item_identifier; - /* Performs file detection. Returns 1 if every path in potential_paths is valid, 0 otherwise. If test_all is true, tests every path; otherwise stops as soon as it reaches an invalid path. */ + // Performs file detection. Returns 1 if every path in potential_paths is valid, 0 otherwise. If + // test_all is true, tests every path; otherwise stops as soon as it reaches an invalid path. int perform_file_detection(bool test_all); - /* Determine whether the given paths are all valid */ + // Determine whether the given paths are all valid. bool paths_are_valid(const path_list_t &paths); }; - #endif From 0fd3f5c0ddf830162b66067e536bdbf0975523e5 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Thu, 14 Apr 2016 22:21:36 -0700 Subject: [PATCH 111/363] Update to pcre2 10.21 Point build tools at 10.21 --- Makefile.in | 2 +- build_tools/lint.fish | 6 +- configure.ac | 2 +- pcre2-10.20/COPYING | 674 --- pcre2-10.20/NEWS | 47 - pcre2-10.20/src/pcre2_substitute.c | 315 -- {pcre2-10.20 => pcre2-10.21}/.gitignore | 2 +- {pcre2-10.20 => pcre2-10.21}/132html | 2 +- {pcre2-10.20 => pcre2-10.21}/AUTHORS | 6 +- {pcre2-10.20 => pcre2-10.21}/CMakeLists.txt | 16 +- pcre2-10.21/COPYING | 5 + {pcre2-10.20 => pcre2-10.21}/ChangeLog | 391 ++ {pcre2-10.20 => pcre2-10.21}/CheckMan | 0 {pcre2-10.20 => pcre2-10.21}/CleanTxt | 0 {pcre2-10.20 => pcre2-10.21}/Detrail | 0 {pcre2-10.20 => pcre2-10.21}/HACKING | 0 {pcre2-10.20 => pcre2-10.21}/INSTALL | 0 {pcre2-10.20 => pcre2-10.21}/LICENCE | 6 +- {pcre2-10.20 => pcre2-10.21}/Makefile.am | 30 +- {pcre2-10.20 => pcre2-10.21}/Makefile.in | 79 +- pcre2-10.21/NEWS | 88 + .../NON-AUTOTOOLS-BUILD | 3 +- {pcre2-10.20 => pcre2-10.21}/PrepareRelease | 12 +- {pcre2-10.20 => pcre2-10.21}/README | 10 +- {pcre2-10.20 => pcre2-10.21}/RunGrepTest | 27 +- {pcre2-10.20 => pcre2-10.21}/RunTest | 217 +- {pcre2-10.20 => pcre2-10.21}/RunTest.bat | 119 +- {pcre2-10.20 => pcre2-10.21}/aclocal.m4 | 245 +- {pcre2-10.20 => pcre2-10.21}/ar-lib | 0 .../cmake/COPYING-CMAKE-SCRIPTS | 0 .../cmake/FindEditline.cmake | 0 .../cmake/FindPackageHandleStandardArgs.cmake | 0 .../cmake/FindReadline.cmake | 0 {pcre2-10.20 => pcre2-10.21}/compile | 0 .../config-cmake.h.in | 1 + {pcre2-10.20 => pcre2-10.21}/config.guess | 0 {pcre2-10.20 => pcre2-10.21}/config.sub | 0 {pcre2-10.20 => pcre2-10.21}/configure | 95 +- {pcre2-10.20 => pcre2-10.21}/configure.ac | 35 +- {pcre2-10.20 => pcre2-10.21}/depcomp | 0 {pcre2-10.20 => pcre2-10.21}/install-sh | 0 .../libpcre2-16.pc.in | 0 .../libpcre2-32.pc.in | 0 {pcre2-10.20 => pcre2-10.21}/libpcre2-8.pc.in | 0 .../libpcre2-posix.pc.in | 0 {pcre2-10.20 => pcre2-10.21}/ltmain.sh | 0 {pcre2-10.20 => pcre2-10.21}/m4/ax_pthread.m4 | 0 {pcre2-10.20 => pcre2-10.21}/m4/libtool.m4 | 0 {pcre2-10.20 => pcre2-10.21}/m4/ltoptions.m4 | 0 {pcre2-10.20 => pcre2-10.21}/m4/ltsugar.m4 | 0 {pcre2-10.20 => pcre2-10.21}/m4/ltversion.m4 | 0 .../m4/lt~obsolete.m4 | 0 .../m4/pcre2_visibility.m4 | 0 {pcre2-10.20 => pcre2-10.21}/missing | 0 {pcre2-10.20 => pcre2-10.21}/pcre2-config.in | 0 {pcre2-10.20 => pcre2-10.21}/perltest.sh | 4 +- .../src/config.h.generic | 9 +- {pcre2-10.20 => pcre2-10.21}/src/config.h.in | 3 + {pcre2-10.20 => pcre2-10.21}/src/dftables.c | 6 +- .../src/pcre2.h.generic | 30 +- {pcre2-10.20 => pcre2-10.21}/src/pcre2.h.in | 26 +- .../src/pcre2_auto_possess.c | 60 +- .../src/pcre2_chartables.c.dist | 0 .../src/pcre2_compile.c | 2112 ++++---- .../src/pcre2_config.c | 2 +- .../src/pcre2_context.c | 32 +- .../src/pcre2_dfa_match.c | 87 +- .../src/pcre2_error.c | 32 +- pcre2-10.21/src/pcre2_find_bracket.c | 218 + .../src/pcre2_internal.h | 23 +- .../src/pcre2_intmodedep.h | 41 +- .../src/pcre2_jit_compile.c | 1422 ++++-- .../src/pcre2_jit_match.c | 5 +- .../src/pcre2_jit_misc.c | 2 +- .../src/pcre2_jit_test.c | 21 +- .../src/pcre2_maketables.c | 2 +- .../src/pcre2_match.c | 132 +- .../src/pcre2_match_data.c | 2 +- .../src/pcre2_newline.c | 2 +- .../src/pcre2_ord2utf.c | 2 +- .../src/pcre2_pattern_info.c | 7 +- .../src/pcre2_printint.c | 35 +- .../src/pcre2_serialize.c | 20 +- .../src/pcre2_string_utils.c | 6 +- .../src/pcre2_study.c | 186 +- pcre2-10.21/src/pcre2_substitute.c | 850 ++++ .../src/pcre2_substring.c | 2 +- .../src/pcre2_tables.c | 358 +- {pcre2-10.20 => pcre2-10.21}/src/pcre2_ucd.c | 4520 +++++++++-------- {pcre2-10.20 => pcre2-10.21}/src/pcre2_ucp.h | 11 +- .../src/pcre2_valid_utf.c | 11 +- .../src/pcre2_xclass.c | 4 +- {pcre2-10.20 => pcre2-10.21}/src/pcre2demo.c | 0 {pcre2-10.20 => pcre2-10.21}/src/pcre2grep.c | 20 +- {pcre2-10.20 => pcre2-10.21}/src/pcre2posix.c | 47 +- {pcre2-10.20 => pcre2-10.21}/src/pcre2posix.h | 2 +- {pcre2-10.20 => pcre2-10.21}/src/pcre2test.c | 1050 ++-- .../src/sljit/sljitConfig.h | 0 .../src/sljit/sljitConfigInternal.h | 6 + .../src/sljit/sljitExecAllocator.c | 0 .../src/sljit/sljitLir.c | 0 .../src/sljit/sljitLir.h | 88 +- .../src/sljit/sljitNativeARM_32.c | 0 .../src/sljit/sljitNativeARM_64.c | 0 .../src/sljit/sljitNativeARM_T2_32.c | 0 .../src/sljit/sljitNativeMIPS_32.c | 0 .../src/sljit/sljitNativeMIPS_64.c | 0 .../src/sljit/sljitNativeMIPS_common.c | 0 .../src/sljit/sljitNativePPC_32.c | 0 .../src/sljit/sljitNativePPC_64.c | 0 .../src/sljit/sljitNativePPC_common.c | 0 .../src/sljit/sljitNativeSPARC_32.c | 0 .../src/sljit/sljitNativeSPARC_common.c | 0 .../src/sljit/sljitNativeTILEGX-encoder.c | 0 .../src/sljit/sljitNativeTILEGX_64.c | 311 +- .../src/sljit/sljitNativeX86_32.c | 0 .../src/sljit/sljitNativeX86_64.c | 0 .../src/sljit/sljitNativeX86_common.c | 70 +- .../src/sljit/sljitUtils.c | 0 {pcre2-10.20 => pcre2-10.21}/test-driver | 0 120 files changed, 8534 insertions(+), 5780 deletions(-) delete mode 100644 pcre2-10.20/COPYING delete mode 100644 pcre2-10.20/NEWS delete mode 100644 pcre2-10.20/src/pcre2_substitute.c rename {pcre2-10.20 => pcre2-10.21}/.gitignore (93%) rename {pcre2-10.20 => pcre2-10.21}/132html (99%) rename {pcre2-10.20 => pcre2-10.21}/AUTHORS (82%) rename {pcre2-10.20 => pcre2-10.21}/CMakeLists.txt (97%) create mode 100644 pcre2-10.21/COPYING rename {pcre2-10.20 => pcre2-10.21}/ChangeLog (51%) rename {pcre2-10.20 => pcre2-10.21}/CheckMan (100%) rename {pcre2-10.20 => pcre2-10.21}/CleanTxt (100%) rename {pcre2-10.20 => pcre2-10.21}/Detrail (100%) rename {pcre2-10.20 => pcre2-10.21}/HACKING (100%) rename {pcre2-10.20 => pcre2-10.21}/INSTALL (100%) rename {pcre2-10.20 => pcre2-10.21}/LICENCE (95%) rename {pcre2-10.20 => pcre2-10.21}/Makefile.am (96%) rename {pcre2-10.20 => pcre2-10.21}/Makefile.in (96%) create mode 100644 pcre2-10.21/NEWS rename {pcre2-10.20 => pcre2-10.21}/NON-AUTOTOOLS-BUILD (99%) rename {pcre2-10.20 => pcre2-10.21}/PrepareRelease (96%) rename {pcre2-10.20 => pcre2-10.21}/README (98%) rename {pcre2-10.20 => pcre2-10.21}/RunGrepTest (97%) rename {pcre2-10.20 => pcre2-10.21}/RunTest (76%) rename {pcre2-10.20 => pcre2-10.21}/RunTest.bat (77%) rename {pcre2-10.20 => pcre2-10.21}/aclocal.m4 (89%) rename {pcre2-10.20 => pcre2-10.21}/ar-lib (100%) rename {pcre2-10.20 => pcre2-10.21}/cmake/COPYING-CMAKE-SCRIPTS (100%) rename {pcre2-10.20 => pcre2-10.21}/cmake/FindEditline.cmake (100%) rename {pcre2-10.20 => pcre2-10.21}/cmake/FindPackageHandleStandardArgs.cmake (100%) rename {pcre2-10.20 => pcre2-10.21}/cmake/FindReadline.cmake (100%) rename {pcre2-10.20 => pcre2-10.21}/compile (100%) rename {pcre2-10.20 => pcre2-10.21}/config-cmake.h.in (97%) rename {pcre2-10.20 => pcre2-10.21}/config.guess (100%) rename {pcre2-10.20 => pcre2-10.21}/config.sub (100%) rename {pcre2-10.20 => pcre2-10.21}/configure (99%) rename {pcre2-10.20 => pcre2-10.21}/configure.ac (97%) rename {pcre2-10.20 => pcre2-10.21}/depcomp (100%) rename {pcre2-10.20 => pcre2-10.21}/install-sh (100%) rename {pcre2-10.20 => pcre2-10.21}/libpcre2-16.pc.in (100%) rename {pcre2-10.20 => pcre2-10.21}/libpcre2-32.pc.in (100%) rename {pcre2-10.20 => pcre2-10.21}/libpcre2-8.pc.in (100%) rename {pcre2-10.20 => pcre2-10.21}/libpcre2-posix.pc.in (100%) rename {pcre2-10.20 => pcre2-10.21}/ltmain.sh (100%) rename {pcre2-10.20 => pcre2-10.21}/m4/ax_pthread.m4 (100%) rename {pcre2-10.20 => pcre2-10.21}/m4/libtool.m4 (100%) rename {pcre2-10.20 => pcre2-10.21}/m4/ltoptions.m4 (100%) rename {pcre2-10.20 => pcre2-10.21}/m4/ltsugar.m4 (100%) rename {pcre2-10.20 => pcre2-10.21}/m4/ltversion.m4 (100%) rename {pcre2-10.20 => pcre2-10.21}/m4/lt~obsolete.m4 (100%) rename {pcre2-10.20 => pcre2-10.21}/m4/pcre2_visibility.m4 (100%) rename {pcre2-10.20 => pcre2-10.21}/missing (100%) rename {pcre2-10.20 => pcre2-10.21}/pcre2-config.in (100%) rename {pcre2-10.20 => pcre2-10.21}/perltest.sh (98%) rename {pcre2-10.20 => pcre2-10.21}/src/config.h.generic (98%) rename {pcre2-10.20 => pcre2-10.21}/src/config.h.in (99%) rename {pcre2-10.20 => pcre2-10.21}/src/dftables.c (97%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2.h.generic (95%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2.h.in (96%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2_auto_possess.c (93%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2_chartables.c.dist (100%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2_compile.c (85%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2_config.c (99%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2_context.c (91%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2_dfa_match.c (97%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2_error.c (91%) create mode 100644 pcre2-10.21/src/pcre2_find_bracket.c rename {pcre2-10.20 => pcre2-10.21}/src/pcre2_internal.h (98%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2_intmodedep.h (96%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2_jit_compile.c (91%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2_jit_match.c (97%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2_jit_misc.c (99%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2_jit_test.c (98%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2_maketables.c (98%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2_match.c (98%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2_match_data.c (98%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2_newline.c (98%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2_ord2utf.c (98%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2_pattern_info.c (98%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2_printint.c (95%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2_serialize.c (97%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2_string_utils.c (97%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2_study.c (88%) create mode 100644 pcre2-10.21/src/pcre2_substitute.c rename {pcre2-10.20 => pcre2-10.21}/src/pcre2_substring.c (99%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2_tables.c (79%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2_ucd.c (51%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2_ucp.h (96%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2_valid_utf.c (98%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2_xclass.c (98%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2demo.c (100%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2grep.c (99%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2posix.c (93%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2posix.h (98%) rename {pcre2-10.20 => pcre2-10.21}/src/pcre2test.c (86%) rename {pcre2-10.20 => pcre2-10.21}/src/sljit/sljitConfig.h (100%) rename {pcre2-10.20 => pcre2-10.21}/src/sljit/sljitConfigInternal.h (99%) rename {pcre2-10.20 => pcre2-10.21}/src/sljit/sljitExecAllocator.c (100%) rename {pcre2-10.20 => pcre2-10.21}/src/sljit/sljitLir.c (100%) rename {pcre2-10.20 => pcre2-10.21}/src/sljit/sljitLir.h (97%) rename {pcre2-10.20 => pcre2-10.21}/src/sljit/sljitNativeARM_32.c (100%) rename {pcre2-10.20 => pcre2-10.21}/src/sljit/sljitNativeARM_64.c (100%) rename {pcre2-10.20 => pcre2-10.21}/src/sljit/sljitNativeARM_T2_32.c (100%) rename {pcre2-10.20 => pcre2-10.21}/src/sljit/sljitNativeMIPS_32.c (100%) rename {pcre2-10.20 => pcre2-10.21}/src/sljit/sljitNativeMIPS_64.c (100%) rename {pcre2-10.20 => pcre2-10.21}/src/sljit/sljitNativeMIPS_common.c (100%) rename {pcre2-10.20 => pcre2-10.21}/src/sljit/sljitNativePPC_32.c (100%) rename {pcre2-10.20 => pcre2-10.21}/src/sljit/sljitNativePPC_64.c (100%) rename {pcre2-10.20 => pcre2-10.21}/src/sljit/sljitNativePPC_common.c (100%) rename {pcre2-10.20 => pcre2-10.21}/src/sljit/sljitNativeSPARC_32.c (100%) rename {pcre2-10.20 => pcre2-10.21}/src/sljit/sljitNativeSPARC_common.c (100%) rename {pcre2-10.20 => pcre2-10.21}/src/sljit/sljitNativeTILEGX-encoder.c (100%) rename {pcre2-10.20 => pcre2-10.21}/src/sljit/sljitNativeTILEGX_64.c (92%) rename {pcre2-10.20 => pcre2-10.21}/src/sljit/sljitNativeX86_32.c (100%) rename {pcre2-10.20 => pcre2-10.21}/src/sljit/sljitNativeX86_64.c (100%) rename {pcre2-10.20 => pcre2-10.21}/src/sljit/sljitNativeX86_common.c (97%) rename {pcre2-10.20 => pcre2-10.21}/src/sljit/sljitUtils.c (100%) rename {pcre2-10.20 => pcre2-10.21}/test-driver (100%) diff --git a/Makefile.in b/Makefile.in index 341b9420d..0858c6477 100644 --- a/Makefile.in +++ b/Makefile.in @@ -62,7 +62,7 @@ extra_confdir = @extra_confdir@ # PCRE2_WIDTH = @WCHAR_T_BITS@ -PCRE2_DIR = pcre2-10.20 +PCRE2_DIR = pcre2-10.21 PCRE2_LIBDIR = $(PCRE2_DIR)/.libs PCRE2_LIB = $(PCRE2_LIBDIR)/libpcre2-$(PCRE2_WIDTH).a PCRE2_H = $(PCRE2_DIR)/src/pcre2.h diff --git a/build_tools/lint.fish b/build_tools/lint.fish index 7182322f0..96efc71b5 100755 --- a/build_tools/lint.fish +++ b/build_tools/lint.fish @@ -78,14 +78,14 @@ if set -q c_files[1] oclint-xcodebuild xcodebuild.log > /dev/null end if test $all = yes - oclint-json-compilation-database -e '/pcre2-10.20/' -- -enable-global-analysis 2>& 1 + oclint-json-compilation-database -e '/pcre2-10.21/' -- -enable-global-analysis 2>& 1 else set i_files for f in $c_files set i_files $i_files -i $f end - echo oclint-json-compilation-database -e '/pcre2-10.20/' $i_files - oclint-json-compilation-database -e '/pcre2-10.20/' $i_files 2>& 1 + echo oclint-json-compilation-database -e '/pcre2-10.21/' $i_files + oclint-json-compilation-database -e '/pcre2-10.21/' $i_files 2>& 1 end else # Presumably we're on Linux or other platform not requiring special diff --git a/configure.ac b/configure.ac index 2a96f63be..90065e9ba 100644 --- a/configure.ac +++ b/configure.ac @@ -849,7 +849,7 @@ if test "x$included_pcre2" = "xyes"; then AC_MSG_NOTICE([using included PCRE2 library]) # unfortunately these get added to the global configuration ac_configure_args="$ac_configure_args --disable-pcre2-8 --enable-pcre2-$WCHAR_T_BITS --disable-shared" - AC_CONFIG_SUBDIRS([pcre2-10.20]) + AC_CONFIG_SUBDIRS([pcre2-10.21]) PCRE2_CXXFLAGS='-I$(PCRE2_DIR)/src' PCRE2_LIBS='-L$(PCRE2_LIBDIR) -lpcre2-$(PCRE2_WIDTH)' diff --git a/pcre2-10.20/COPYING b/pcre2-10.20/COPYING deleted file mode 100644 index 94a9ed024..000000000 --- a/pcre2-10.20/COPYING +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/pcre2-10.20/NEWS b/pcre2-10.20/NEWS deleted file mode 100644 index 84f051355..000000000 --- a/pcre2-10.20/NEWS +++ /dev/null @@ -1,47 +0,0 @@ -News about PCRE2 releases -------------------------- - -Version 10.20 30-June-2015 --------------------------- - -1. Callouts with string arguments and the pcre2_callout_enumerate() function -have been implemented. - -2. The PCRE2_NEVER_BACKSLASH_C option, which locks out the use of \C, is added. - -3. The PCRE2_ALT_CIRCUMFLEX option lets ^ match after a newline at the end of a -subject in multiline mode. - -4. The way named subpatterns are handled has been refactored. The previous -approach had several bugs. - -5. The handling of \c in EBCDIC environments has been changed to conform to the -perlebcdic document. This is an incompatible change. - -6. Bugs have been mended, many of them discovered by fuzzers. - - -Version 10.10 06-March-2015 ---------------------------- - -1. Serialization and de-serialization functions have been added to the API, -making it possible to save and restore sets of compiled patterns, though -restoration must be done in the same environment that was used for compilation. - -2. The (*NO_JIT) feature has been added; this makes it possible for a pattern -creator to specify that JIT is not to be used. - -3. A number of bugs have been fixed. In particular, bugs that caused building -on Windows using CMake to fail have been mended. - - -Version 10.00 05-January-2015 ------------------------------ - -Version 10.00 is the first release of PCRE2, a revised API for the PCRE -library. Changes prior to 10.00 are logged in the ChangeLog file for the old -API, up to item 20 for release 8.36. New programs are recommended to use the -new library. Programs that use the original (PCRE1) API will need changing -before linking with the new library. - -**** diff --git a/pcre2-10.20/src/pcre2_substitute.c b/pcre2-10.20/src/pcre2_substitute.c deleted file mode 100644 index ec00ebb86..000000000 --- a/pcre2-10.20/src/pcre2_substitute.c +++ /dev/null @@ -1,315 +0,0 @@ -/************************************************* -* Perl-Compatible Regular Expressions * -*************************************************/ - -/* PCRE is a library of functions to support regular expressions whose syntax -and semantics are as close as possible to those of the Perl 5 language. - - Written by Philip Hazel - Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2014 University of Cambridge - ------------------------------------------------------------------------------ -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - * Neither the name of the University of Cambridge nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------------ -*/ - - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "pcre2_internal.h" - - -/************************************************* -* Match and substitute * -*************************************************/ - -/* This function applies a compiled re to a subject string and creates a new -string with substitutions. The first 7 arguments are the same as for -pcre2_match(). Either string length may be PCRE2_ZERO_TERMINATED. - -Arguments: - code points to the compiled expression - subject points to the subject string - length length of subject string (may contain binary zeros) - start_offset where to start in the subject string - options option bits - match_data points to a match_data block, or is NULL - context points a PCRE2 context - replacement points to the replacement string - rlength length of replacement string - buffer where to put the substituted string - blength points to length of buffer; updated to length of string - -Returns: >= 0 number of substitutions made - < 0 an error code - PCRE2_ERROR_BADREPLACEMENT means invalid use of $ -*/ - -PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION -pcre2_substitute(const pcre2_code *code, PCRE2_SPTR subject, PCRE2_SIZE length, - PCRE2_SIZE start_offset, uint32_t options, pcre2_match_data *match_data, - pcre2_match_context *mcontext, PCRE2_SPTR replacement, PCRE2_SIZE rlength, - PCRE2_UCHAR *buffer, PCRE2_SIZE *blength) -{ -int rc; -int subs; -uint32_t ovector_count; -uint32_t goptions = 0; -BOOL match_data_created = FALSE; -BOOL global = FALSE; -PCRE2_SIZE buff_offset, lengthleft, fraglength; -PCRE2_SIZE *ovector; - -/* Partial matching is not valid. */ - -if ((options & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) != 0) - return PCRE2_ERROR_BADOPTION; - -/* If no match data block is provided, create one. */ - -if (match_data == NULL) - { - pcre2_general_context *gcontext = (mcontext == NULL)? - (pcre2_general_context *)code : - (pcre2_general_context *)mcontext; - match_data = pcre2_match_data_create_from_pattern(code, gcontext); - if (match_data == NULL) return PCRE2_ERROR_NOMEMORY; - match_data_created = TRUE; - } -ovector = pcre2_get_ovector_pointer(match_data); -ovector_count = pcre2_get_ovector_count(match_data); - -/* Check UTF replacement string if necessary. */ - -#ifdef SUPPORT_UNICODE -if ((code->overall_options & PCRE2_UTF) != 0 && - (options & PCRE2_NO_UTF_CHECK) == 0) - { - rc = PRIV(valid_utf)(replacement, rlength, &(match_data->rightchar)); - if (rc != 0) - { - match_data->leftchar = 0; - goto EXIT; - } - } -#endif /* SUPPORT_UNICODE */ - -/* Notice the global option and remove it from the options that are passed to -pcre2_match(). */ - -if ((options & PCRE2_SUBSTITUTE_GLOBAL) != 0) - { - options &= ~PCRE2_SUBSTITUTE_GLOBAL; - global = TRUE; - } - -/* Find lengths of zero-terminated strings. */ - -if (length == PCRE2_ZERO_TERMINATED) length = PRIV(strlen)(subject); -if (rlength == PCRE2_ZERO_TERMINATED) rlength = PRIV(strlen)(replacement); - -/* Copy up to the start offset */ - -if (start_offset > *blength) goto NOROOM; -memcpy(buffer, subject, start_offset * (PCRE2_CODE_UNIT_WIDTH/8)); -buff_offset = start_offset; -lengthleft = *blength - start_offset; - -/* Loop for global substituting. */ - -subs = 0; -do - { - PCRE2_SIZE i; - - rc = pcre2_match(code, subject, length, start_offset, options|goptions, - match_data, mcontext); - - /* Any error other than no match returns the error code. No match when not - doing the special after-empty-match global rematch, or when at the end of the - subject, breaks the global loop. Otherwise, advance the starting point by one - character, copying it to the output, and try again. */ - - if (rc < 0) - { - PCRE2_SIZE save_start; - - if (rc != PCRE2_ERROR_NOMATCH) goto EXIT; - if (goptions == 0 || start_offset >= length) break; - - save_start = start_offset++; - if ((code->overall_options & PCRE2_UTF) != 0) - { -#if PCRE2_CODE_UNIT_WIDTH == 8 - while (start_offset < length && (subject[start_offset] & 0xc0) == 0x80) - start_offset++; -#elif PCRE2_CODE_UNIT_WIDTH == 16 - while (start_offset < length && - (subject[start_offset] & 0xfc00) == 0xdc00) - start_offset++; -#endif - } - - fraglength = start_offset - save_start; - if (lengthleft < fraglength) goto NOROOM; - memcpy(buffer + buff_offset, subject + save_start, - fraglength*(PCRE2_CODE_UNIT_WIDTH/8)); - buff_offset += fraglength; - lengthleft -= fraglength; - - goptions = 0; - continue; - } - - /* Handle a successful match. */ - - subs++; - if (rc == 0) rc = ovector_count; - fraglength = ovector[0] - start_offset; - if (fraglength >= lengthleft) goto NOROOM; - memcpy(buffer + buff_offset, subject + start_offset, - fraglength*(PCRE2_CODE_UNIT_WIDTH/8)); - buff_offset += fraglength; - lengthleft -= fraglength; - - for (i = 0; i < rlength; i++) - { - if (replacement[i] == CHAR_DOLLAR_SIGN) - { - int group, n; - BOOL inparens; - PCRE2_SIZE sublength; - PCRE2_UCHAR next; - PCRE2_UCHAR name[33]; - - if (++i == rlength) goto BAD; - if ((next = replacement[i]) == CHAR_DOLLAR_SIGN) goto LITERAL; - - group = -1; - n = 0; - inparens = FALSE; - - if (next == CHAR_LEFT_CURLY_BRACKET) - { - if (++i == rlength) goto BAD; - next = replacement[i]; - inparens = TRUE; - } - - if (next >= CHAR_0 && next <= CHAR_9) - { - group = next - CHAR_0; - while (++i < rlength) - { - next = replacement[i]; - if (next < CHAR_0 || next > CHAR_9) break; - group = group * 10 + next - CHAR_0; - } - } - else - { - const uint8_t *ctypes = code->tables + ctypes_offset; - while (MAX_255(next) && (ctypes[next] & ctype_word) != 0) - { - name[n++] = next; - if (n > 32) goto BAD; - if (i == rlength) break; - next = replacement[++i]; - } - if (n == 0) goto BAD; - name[n] = 0; - } - - if (inparens) - { - if (i == rlength || next != CHAR_RIGHT_CURLY_BRACKET) goto BAD; - } - else i--; /* Last code unit of name/number */ - - /* Have found a syntactically correct group number or name. */ - - sublength = lengthleft; - if (group < 0) - rc = pcre2_substring_copy_byname(match_data, name, - buffer + buff_offset, &sublength); - else - rc = pcre2_substring_copy_bynumber(match_data, group, - buffer + buff_offset, &sublength); - - if (rc < 0) goto EXIT; - buff_offset += sublength; - lengthleft -= sublength; - } - - /* Handle a literal code unit */ - - else - { - LITERAL: - if (lengthleft-- < 1) goto NOROOM; - buffer[buff_offset++] = replacement[i]; - } - } - - /* The replacement has been copied to the output. Update the start offset to - point to the rest of the subject string. If we matched an empty string, - do the magic for global matches. */ - - start_offset = ovector[1]; - goptions = (ovector[0] != ovector[1])? 0 : - PCRE2_ANCHORED|PCRE2_NOTEMPTY_ATSTART; - } while (global); /* Repeat "do" loop */ - -/* Copy the rest of the subject and return the number of substitutions. */ - -rc = subs; -fraglength = length - start_offset; -if (fraglength + 1 > lengthleft) goto NOROOM; -memcpy(buffer + buff_offset, subject + start_offset, - fraglength*(PCRE2_CODE_UNIT_WIDTH/8)); -buff_offset += fraglength; -buffer[buff_offset] = 0; -*blength = buff_offset; - -EXIT: -if (match_data_created) pcre2_match_data_free(match_data); - else match_data->rc = rc; -return rc; - -NOROOM: -rc = PCRE2_ERROR_NOMEMORY; -goto EXIT; - -BAD: -rc = PCRE2_ERROR_BADREPLACEMENT; -goto EXIT; -} - -/* End of pcre2_substitute.c */ diff --git a/pcre2-10.20/.gitignore b/pcre2-10.21/.gitignore similarity index 93% rename from pcre2-10.20/.gitignore rename to pcre2-10.21/.gitignore index 0b7a536be..fa91befa7 100644 --- a/pcre2-10.20/.gitignore +++ b/pcre2-10.21/.gitignore @@ -12,4 +12,4 @@ pcre2_chartables.c pcre2-config pcre2test pcre2.h -stamp-h1 +stamp-h1 \ No newline at end of file diff --git a/pcre2-10.20/132html b/pcre2-10.21/132html similarity index 99% rename from pcre2-10.20/132html rename to pcre2-10.21/132html index b06259848..3a16a59e0 100755 --- a/pcre2-10.20/132html +++ b/pcre2-10.21/132html @@ -148,7 +148,7 @@ while () printf("

  • $title\n", $ref, $ref); printf TEMP ("
    $title
    \n", - $ref, $ref); + $ref); $ref++; } else diff --git a/pcre2-10.20/AUTHORS b/pcre2-10.21/AUTHORS similarity index 82% rename from pcre2-10.20/AUTHORS rename to pcre2-10.21/AUTHORS index 14a1a19fd..d9a0e1569 100644 --- a/pcre2-10.20/AUTHORS +++ b/pcre2-10.21/AUTHORS @@ -8,7 +8,7 @@ Email domain: cam.ac.uk University of Cambridge Computing Service, Cambridge, England. -Copyright (c) 1997-2015 University of Cambridge +Copyright (c) 1997-2016 University of Cambridge All rights reserved @@ -19,7 +19,7 @@ Written by: Zoltan Herczeg Email local part: hzmester Emain domain: freemail.hu -Copyright(c) 2010-2015 Zoltan Herczeg +Copyright(c) 2010-2016 Zoltan Herczeg All rights reserved. @@ -30,7 +30,7 @@ Written by: Zoltan Herczeg Email local part: hzmester Emain domain: freemail.hu -Copyright(c) 2009-2015 Zoltan Herczeg +Copyright(c) 2009-2016 Zoltan Herczeg All rights reserved. #### diff --git a/pcre2-10.20/CMakeLists.txt b/pcre2-10.21/CMakeLists.txt similarity index 97% rename from pcre2-10.20/CMakeLists.txt rename to pcre2-10.21/CMakeLists.txt index b76a95309..2c84b054d 100644 --- a/pcre2-10.20/CMakeLists.txt +++ b/pcre2-10.21/CMakeLists.txt @@ -67,7 +67,10 @@ # 2013-10-08 PH got rid of the "source" command, which is a bash-ism (use ".") # 2013-11-05 PH added support for PARENS_NEST_LIMIT # 2014-08-29 PH converted the file for PCRE2 (which has no C++). -# 2015-04024 PH added support for PCRE2_DEBUG +# 2015-04-24 PH added support for PCRE2_DEBUG +# 2015-07-16 PH updated for new pcre2_find_bracket source module +# 2015-08-24 PH correct C_FLAGS setting (patch from Roy Ivy III) +# 2015-10=16 PH added support for never-backslash-C PROJECT(PCRE2 C) @@ -79,7 +82,7 @@ CMAKE_POLICY(SET CMP0026 OLD) SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) # for FindReadline.cmake -SET(CMAKE_C_FLAGS -I${PROJECT_SOURCE_DIR}/src) +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${PROJECT_SOURCE_DIR}/src") # external packages FIND_PACKAGE( BZip2 ) @@ -160,6 +163,9 @@ SET(PCRE2_SUPPORT_UNICODE ON CACHE BOOL SET(PCRE2_SUPPORT_BSR_ANYCRLF OFF CACHE BOOL "ON=Backslash-R matches only LF CR and CRLF, OFF=Backslash-R matches all Unicode Linebreaks") +SET(PCRE2_NEVER_BACKSLASH_C OFF CACHE BOOL + "If ON, backslash-C (upper case C) is locked out.") + SET(PCRE2_SUPPORT_VALGRIND OFF CACHE BOOL "Enable Valgrind support.") @@ -250,6 +256,10 @@ IF(PCRE2_SUPPORT_BSR_ANYCRLF) SET(BSR_ANYCRLF 1) ENDIF(PCRE2_SUPPORT_BSR_ANYCRLF) +IF(PCRE2_NEVER_BACKSLASH_C) + SET(NEVER_BACKSLASH_C 1) +ENDIF(PCRE2_NEVER_BACKSLASH_C) + IF(PCRE2_SUPPORT_UNICODE) SET(SUPPORT_UNICODE 1) ENDIF(PCRE2_SUPPORT_UNICODE) @@ -390,6 +400,7 @@ SET(PCRE2_SOURCES src/pcre2_context.c src/pcre2_dfa_match.c src/pcre2_error.c + src/pcre2_find_bracket.c src/pcre2_jit_compile.c src/pcre2_maketables.c src/pcre2_match.c @@ -716,6 +727,7 @@ IF(PCRE2_SHOW_REPORT) MESSAGE(STATUS " Enable Unicode support .......... : ${PCRE2_SUPPORT_UNICODE}") MESSAGE(STATUS " Newline char/sequence ........... : ${PCRE2_NEWLINE}") MESSAGE(STATUS " \\R matches only ANYCRLF ......... : ${PCRE2_SUPPORT_BSR_ANYCRLF}") + MESSAGE(STATUS " \\C is disabled .................. : ${PCRE2_NEVER_BACKSLASH_C}") MESSAGE(STATUS " EBCDIC coding ................... : ${PCRE2_EBCDIC}") MESSAGE(STATUS " EBCDIC coding with NL=0x25 ...... : ${PCRE2_EBCDIC_NL25}") MESSAGE(STATUS " Rebuild char tables ............. : ${PCRE2_REBUILD_CHARTABLES}") diff --git a/pcre2-10.21/COPYING b/pcre2-10.21/COPYING new file mode 100644 index 000000000..c233950f6 --- /dev/null +++ b/pcre2-10.21/COPYING @@ -0,0 +1,5 @@ +PCRE2 LICENCE + +Please see the file LICENCE in the PCRE2 distribution for licensing details. + +End diff --git a/pcre2-10.20/ChangeLog b/pcre2-10.21/ChangeLog similarity index 51% rename from pcre2-10.20/ChangeLog rename to pcre2-10.21/ChangeLog index 196e95320..2035490c6 100644 --- a/pcre2-10.20/ChangeLog +++ b/pcre2-10.21/ChangeLog @@ -1,6 +1,397 @@ Change Log for PCRE2 -------------------- +Version 10.21 12-January-2016 +----------------------------- + +1. Improve matching speed of patterns starting with + or * in JIT. + +2. Use memchr() to find the first character in an unanchored match in 8-bit +mode in the interpreter. This gives a significant speed improvement. + +3. Removed a redundant copy of the opcode_possessify table in the +pcre2_auto_possessify.c source. + +4. Fix typos in dftables.c for z/OS. + +5. Change 36 for 10.20 broke the handling of [[:>:]] and [[:<:]] in that +processing them could involve a buffer overflow if the following character was +an opening parenthesis. + +6. Change 36 for 10.20 also introduced a bug in processing this pattern: +/((?x)(*:0))#(?'/. Specifically: if a setting of (?x) was followed by a (*MARK) +setting (which (*:0) is), then (?x) did not get unset at the end of its group +during the scan for named groups, and hence the external # was incorrectly +treated as a comment and the invalid (?' at the end of the pattern was not +diagnosed. This caused a buffer overflow during the real compile. This bug was +discovered by Karl Skomski with the LLVM fuzzer. + +7. Moved the pcre2_find_bracket() function from src/pcre2_compile.c into its +own source module to avoid a circular dependency between src/pcre2_compile.c +and src/pcre2_study.c + +8. A callout with a string argument containing an opening square bracket, for +example /(?C$[$)(?<]/, was incorrectly processed and could provoke a buffer +overflow. This bug was discovered by Karl Skomski with the LLVM fuzzer. + +9. The handling of callouts during the pre-pass for named group identification +has been tightened up. + +10. The quantifier {1} can be ignored, whether greedy, non-greedy, or +possessive. This is a very minor optimization. + +11. A possessively repeated conditional group that could match an empty string, +for example, /(?(R))*+/, was incorrectly compiled. + +12. The Unicode tables have been updated to Unicode 8.0.0 (thanks to Christian +Persch). + +13. An empty comment (?#) in a pattern was incorrectly processed and could +provoke a buffer overflow. This bug was discovered by Karl Skomski with the +LLVM fuzzer. + +14. Fix infinite recursion in the JIT compiler when certain patterns such as +/(?:|a|){100}x/ are analysed. + +15. Some patterns with character classes involving [: and \\ were incorrectly +compiled and could cause reading from uninitialized memory or an incorrect +error diagnosis. Examples are: /[[:\\](?<[::]/ and /[[:\\](?'abc')[a:]. The +first of these bugs was discovered by Karl Skomski with the LLVM fuzzer. + +16. Pathological patterns containing many nested occurrences of [: caused +pcre2_compile() to run for a very long time. This bug was found by the LLVM +fuzzer. + +17. A missing closing parenthesis for a callout with a string argument was not +being diagnosed, possibly leading to a buffer overflow. This bug was found by +the LLVM fuzzer. + +18. A conditional group with only one branch has an implicit empty alternative +branch and must therefore be treated as potentially matching an empty string. + +19. If (?R was followed by - or + incorrect behaviour happened instead of a +diagnostic. This bug was discovered by Karl Skomski with the LLVM fuzzer. + +20. Another bug that was introduced by change 36 for 10.20: conditional groups +whose condition was an assertion preceded by an explicit callout with a string +argument might be incorrectly processed, especially if the string contained \Q. +This bug was discovered by Karl Skomski with the LLVM fuzzer. + +21. Compiling PCRE2 with the sanitize options of clang showed up a number of +very pedantic coding infelicities and a buffer overflow while checking a UTF-8 +string if the final multi-byte UTF-8 character was truncated. + +22. For Perl compatibility in EBCDIC environments, ranges such as a-z in a +class, where both values are literal letters in the same case, omit the +non-letter EBCDIC code points within the range. + +23. Finding the minimum matching length of complex patterns with back +references and/or recursions can take a long time. There is now a cut-off that +gives up trying to find a minimum length when things get too complex. + +24. An optimization has been added that speeds up finding the minimum matching +length for patterns containing repeated capturing groups or recursions. + +25. If a pattern contained a back reference to a group whose number was +duplicated as a result of appearing in a (?|...) group, the computation of the +minimum matching length gave a wrong result, which could cause incorrect "no +match" errors. For such patterns, a minimum matching length cannot at present +be computed. + +26. Added a check for integer overflow in conditions (?() and +(?(R). This omission was discovered by Karl Skomski with the LLVM +fuzzer. + +27. Fixed an issue when \p{Any} inside an xclass did not read the current +character. + +28. If pcre2grep was given the -q option with -c or -l, or when handling a +binary file, it incorrectly wrote output to stdout. + +29. The JIT compiler did not restore the control verb head in case of *THEN +control verbs. This issue was found by Karl Skomski with a custom LLVM fuzzer. + +30. The way recursive references such as (?3) are compiled has been re-written +because the old way was the cause of many issues. Now, conversion of the group +number into a pattern offset does not happen until the pattern has been +completely compiled. This does mean that detection of all infinitely looping +recursions is postponed till match time. In the past, some easy ones were +detected at compile time. This re-writing was done in response to yet another +bug found by the LLVM fuzzer. + +31. A test for a back reference to a non-existent group was missing for items +such as \987. This caused incorrect code to be compiled. This issue was found +by Karl Skomski with a custom LLVM fuzzer. + +32. Error messages for syntax errors following \g and \k were giving inaccurate +offsets in the pattern. + +33. Improve the performance of starting single character repetitions in JIT. + +34. (*LIMIT_MATCH=) now gives an error instead of setting the value to 0. + +35. Error messages for syntax errors in *LIMIT_MATCH and *LIMIT_RECURSION now +give the right offset instead of zero. + +36. The JIT compiler should not check repeats after a {0,1} repeat byte code. +This issue was found by Karl Skomski with a custom LLVM fuzzer. + +37. The JIT compiler should restore the control chain for empty possessive +repeats. This issue was found by Karl Skomski with a custom LLVM fuzzer. + +38. A bug which was introduced by the single character repetition optimization +was fixed. + +39. Match limit check added to recursion. This issue was found by Karl Skomski +with a custom LLVM fuzzer. + +40. Arrange for the UTF check in pcre2_match() and pcre2_dfa_match() to look +only at the part of the subject that is relevant when the starting offset is +non-zero. + +41. Improve first character match in JIT with SSE2 on x86. + +42. Fix two assertion fails in JIT. These issues were found by Karl Skomski +with a custom LLVM fuzzer. + +43. Correct the setting of CMAKE_C_FLAGS in CMakeLists.txt (patch from Roy Ivy +III). + +44. Fix bug in RunTest.bat for new test 14, and adjust the script for the added +test (there are now 20 in total). + +45. Fixed a corner case of range optimization in JIT. + +46. Add the ${*MARK} facility to pcre2_substitute(). + +47. Modifier lists in pcre2test were splitting at spaces without the required +commas. + +48. Implemented PCRE2_ALT_VERBNAMES. + +49. Fixed two issues in JIT. These were found by Karl Skomski with a custom +LLVM fuzzer. + +50. The pcre2test program has been extended by adding the #newline_default +command. This has made it possible to run the standard tests when PCRE2 is +compiled with either CR or CRLF as the default newline convention. As part of +this work, the new command was added to several test files and the testing +scripts were modified. The pcre2grep tests can now also be run when there is no +LF in the default newline convention. + +51. The RunTest script has been modified so that, when JIT is used and valgrind +is specified, a valgrind suppressions file is set up to ignore "Invalid read of +size 16" errors because these are false positives when the hardware supports +the SSE2 instruction set. + +52. It is now possible to have comment lines amid the subject strings in +pcre2test (and perltest.sh) input. + +53. Implemented PCRE2_USE_OFFSET_LIMIT and pcre2_set_offset_limit(). + +54. Add the null_context modifier to pcre2test so that calling pcre2_compile() +and the matching functions with NULL contexts can be tested. + +55. Implemented PCRE2_SUBSTITUTE_EXTENDED. + +56. In a character class such as [\W\p{Any}] where both a negative-type escape +("not a word character") and a property escape were present, the property +escape was being ignored. + +57. Fixed integer overflow for patterns whose minimum matching length is very, +very large. + +58. Implemented --never-backslash-C. + +59. Change 55 above introduced a bug by which certain patterns provoked the +erroneous error "\ at end of pattern". + +60. The special sequences [[:<:]] and [[:>:]] gave rise to incorrect compiling +errors or other strange effects if compiled in UCP mode. Found with libFuzzer +and AddressSanitizer. + +61. Whitespace at the end of a pcre2test pattern line caused a spurious error +message if there were only single-character modifiers. It should be ignored. + +62. The use of PCRE2_NO_AUTO_CAPTURE could cause incorrect compilation results +or segmentation errors for some patterns. Found with libFuzzer and +AddressSanitizer. + +63. Very long names in (*MARK) or (*THEN) etc. items could provoke a buffer +overflow. + +64. Improve error message for overly-complicated patterns. + +65. Implemented an optional replication feature for patterns in pcre2test, to +make it easier to test long repetitive patterns. The tests for 63 above are +converted to use the new feature. + +66. In the POSIX wrapper, if regerror() was given too small a buffer, it could +misbehave. + +67. In pcre2_substitute() in UTF mode, the UTF validity check on the +replacement string was happening before the length setting when the replacement +string was zero-terminated. + +68. In pcre2_substitute() in UTF mode, PCRE2_NO_UTF_CHECK can be set for the +second and subsequent calls to pcre2_match(). + +69. There was no check for integer overflow for a replacement group number in +pcre2_substitute(). An added check for a number greater than the largest group +number in the pattern means this is not now needed. + +70. The PCRE2-specific VERSION condition didn't work correctly if only one +digit was given after the decimal point, or if more than two digits were given. +It now works with one or two digits, and gives a compile time error if more are +given. + +71. In pcre2_substitute() there was the possibility of reading one code unit +beyond the end of the replacement string. + +72. The code for checking a subject's UTF-32 validity for a pattern with a +lookbehind involved an out-of-bounds pointer, which could potentially cause +trouble in some environments. + +73. The maximum lookbehind length was incorrectly calculated for patterns such +as /(?<=(a)(?-1))x/ which have a recursion within a backreference. + +74. Give an error if a lookbehind assertion is longer than 65535 code units. + +75. Give an error in pcre2_substitute() if a match ends before it starts (as a +result of the use of \K). + +76. Check the length of subpattern names and the names in (*MARK:xx) etc. +dynamically to avoid the possibility of integer overflow. + +77. Implement pcre2_set_max_pattern_length() so that programs can restrict the +size of patterns that they are prepared to handle. + +78. (*NO_AUTO_POSSESS) was not working. + +79. Adding group information caching improves the speed of compiling when +checking whether a group has a fixed length and/or could match an empty string, +especially when recursion or subroutine calls are involved. However, this +cannot be used when (?| is present in the pattern because the same number may +be used for groups of different sizes. To catch runaway patterns in this +situation, counts have been introduced to the functions that scan for empty +branches or compute fixed lengths. + +80. Allow for the possibility of the size of the nest_save structure not being +a factor of the size of the compiling workspace (it currently is). + +81. Check for integer overflow in minimum length calculation and cap it at +65535. + +82. Small optimizations in code for finding the minimum matching length. + +83. Lock out configuring for EBCDIC with non-8-bit libraries. + +84. Test for error code <= 0 in regerror(). + +85. Check for too many replacements (more than INT_MAX) in pcre2_substitute(). + +86. Avoid the possibility of computing with an out-of-bounds pointer (though +not dereferencing it) while handling lookbehind assertions. + +87. Failure to get memory for the match data in regcomp() is now given as a +regcomp() error instead of waiting for regexec() to pick it up. + +88. In pcre2_substitute(), ensure that CRLF is not split when it is a valid +newline sequence. + +89. Paranoid check in regcomp() for bad error code from pcre2_compile(). + +90. Run test 8 (internal offsets and code sizes) for link sizes 3 and 4 as well +as for link size 2. + +91. Document that JIT has a limit on pattern size, and give more information +about JIT compile failures in pcre2test. + +92. Implement PCRE2_INFO_HASBACKSLASHC. + +93. Re-arrange valgrind support code in pcre2test to avoid spurious reports +with JIT (possibly caused by SSE2?). + +94. Support offset_limit in JIT. + +95. A sequence such as [[:punct:]b] that is, a POSIX character class followed +by a single ASCII character in a class item, was incorrectly compiled in UCP +mode. The POSIX class got lost, but only if the single character followed it. + +96. [:punct:] in UCP mode was matching some characters in the range 128-255 +that should not have been matched. + +97. If [:^ascii:] or [:^xdigit:] are present in a non-negated class, all +characters with code points greater than 255 are in the class. When a Unicode +property was also in the class (if PCRE2_UCP is set, escapes such as \w are +turned into Unicode properties), wide characters were not correctly handled, +and could fail to match. + +98. In pcre2test, make the "startoffset" modifier a synonym of "offset", +because it sets the "startoffset" parameter for pcre2_match(). + +99. If PCRE2_AUTO_CALLOUT was set on a pattern that had a (?# comment between +an item and its qualifier (for example, A(?#comment)?B) pcre2_compile() +misbehaved. This bug was found by the LLVM fuzzer. + +100. The error for an invalid UTF pattern string always gave the code unit +offset as zero instead of where the invalidity was found. + +101. Further to 97 above, negated classes such as [^[:^ascii:]\d] were also not +working correctly in UCP mode. + +102. Similar to 99 above, if an isolated \E was present between an item and its +qualifier when PCRE2_AUTO_CALLOUT was set, pcre2_compile() misbehaved. This bug +was found by the LLVM fuzzer. + +103. The POSIX wrapper function regexec() crashed if the option REG_STARTEND +was set when the pmatch argument was NULL. It now returns REG_INVARG. + +104. Allow for up to 32-bit numbers in the ordin() function in pcre2grep. + +105. An empty \Q\E sequence between an item and its qualifier caused +pcre2_compile() to misbehave when auto callouts were enabled. This bug +was found by the LLVM fuzzer. + +106. If both PCRE2_ALT_VERBNAMES and PCRE2_EXTENDED were set, and a (*MARK) or +other verb "name" ended with whitespace immediately before the closing +parenthesis, pcre2_compile() misbehaved. Example: /(*:abc )/, but only when +both those options were set. + +107. In a number of places pcre2_compile() was not handling NULL characters +correctly, and pcre2test with the "bincode" modifier was not always correctly +displaying fields containing NULLS: + + (a) Within /x extended #-comments + (b) Within the "name" part of (*MARK) and other *verbs + (c) Within the text argument of a callout + +108. If a pattern that was compiled with PCRE2_EXTENDED started with white +space or a #-type comment that was followed by (?-x), which turns off +PCRE2_EXTENDED, and there was no subsequent (?x) to turn it on again, +pcre2_compile() assumed that (?-x) applied to the whole pattern and +consequently mis-compiled it. This bug was found by the LLVM fuzzer. The fix +for this bug means that a setting of any of the (?imsxU) options at the start +of a pattern is no longer transferred to the options that are returned by +PCRE2_INFO_ALLOPTIONS. In fact, this was an anachronism that should have +changed when the effects of those options were all moved to compile time. + +109. An escaped closing parenthesis in the "name" part of a (*verb) when +PCRE2_ALT_VERBNAMES was set caused pcre2_compile() to malfunction. This bug +was found by the LLVM fuzzer. + +110. Implemented PCRE2_SUBSTITUTE_UNSET_EMPTY, and updated pcre2test to make it +possible to test it. + +111. "Harden" pcre2test against ridiculously large values in modifiers and +command line arguments. + +112. Implemented PCRE2_SUBSTITUTE_UNKNOWN_UNSET and PCRE2_SUBSTITUTE_OVERFLOW_ +LENGTH. + +113. Fix printing of *MARK names that contain binary zeroes in pcre2test. + + Version 10.20 30-June-2015 -------------------------- diff --git a/pcre2-10.20/CheckMan b/pcre2-10.21/CheckMan similarity index 100% rename from pcre2-10.20/CheckMan rename to pcre2-10.21/CheckMan diff --git a/pcre2-10.20/CleanTxt b/pcre2-10.21/CleanTxt similarity index 100% rename from pcre2-10.20/CleanTxt rename to pcre2-10.21/CleanTxt diff --git a/pcre2-10.20/Detrail b/pcre2-10.21/Detrail similarity index 100% rename from pcre2-10.20/Detrail rename to pcre2-10.21/Detrail diff --git a/pcre2-10.20/HACKING b/pcre2-10.21/HACKING similarity index 100% rename from pcre2-10.20/HACKING rename to pcre2-10.21/HACKING diff --git a/pcre2-10.20/INSTALL b/pcre2-10.21/INSTALL similarity index 100% rename from pcre2-10.20/INSTALL rename to pcre2-10.21/INSTALL diff --git a/pcre2-10.20/LICENCE b/pcre2-10.21/LICENCE similarity index 95% rename from pcre2-10.20/LICENCE rename to pcre2-10.21/LICENCE index 30416d843..6600a6590 100644 --- a/pcre2-10.20/LICENCE +++ b/pcre2-10.21/LICENCE @@ -25,7 +25,7 @@ Email domain: cam.ac.uk University of Cambridge Computing Service, Cambridge, England. -Copyright (c) 1997-2015 University of Cambridge +Copyright (c) 1997-2016 University of Cambridge All rights reserved. @@ -36,7 +36,7 @@ Written by: Zoltan Herczeg Email local part: hzmester Emain domain: freemail.hu -Copyright(c) 2010-2015 Zoltan Herczeg +Copyright(c) 2010-2016 Zoltan Herczeg All rights reserved. @@ -47,7 +47,7 @@ Written by: Zoltan Herczeg Email local part: hzmester Emain domain: freemail.hu -Copyright(c) 2009-2015 Zoltan Herczeg +Copyright(c) 2009-2016 Zoltan Herczeg All rights reserved. diff --git a/pcre2-10.20/Makefile.am b/pcre2-10.21/Makefile.am similarity index 96% rename from pcre2-10.20/Makefile.am rename to pcre2-10.21/Makefile.am index 56f93db2e..5977ba06b 100644 --- a/pcre2-10.20/Makefile.am +++ b/pcre2-10.21/Makefile.am @@ -64,6 +64,7 @@ dist_html_DATA = \ doc/html/pcre2_set_character_tables.html \ doc/html/pcre2_set_compile_recursion_guard.html \ doc/html/pcre2_set_match_limit.html \ + doc/html/pcre2_set_offset_limit.html \ doc/html/pcre2_set_newline.html \ doc/html/pcre2_set_parens_nest_limit.html \ doc/html/pcre2_set_recursion_limit.html \ @@ -143,6 +144,7 @@ dist_man_MANS = \ doc/pcre2_set_character_tables.3 \ doc/pcre2_set_compile_recursion_guard.3 \ doc/pcre2_set_match_limit.3 \ + doc/pcre2_set_offset_limit.3 \ doc/pcre2_set_newline.3 \ doc/pcre2_set_parens_nest_limit.3 \ doc/pcre2_set_recursion_limit.3 \ @@ -319,6 +321,7 @@ COMMON_SOURCES = \ src/pcre2_context.c \ src/pcre2_dfa_match.c \ src/pcre2_error.c \ + src/pcre2_find_bracket.c \ src/pcre2_internal.h \ src/pcre2_intmodedep.h \ src/pcre2_jit_compile.c \ @@ -586,6 +589,10 @@ EXTRA_DIST += \ testdata/testinput17 \ testdata/testinput18 \ testdata/testinput19 \ + testdata/testinput20 \ + testdata/testinput21 \ + testdata/testinput22 \ + testdata/testinput23 \ testdata/testinputEBC \ testdata/testoutput1 \ testdata/testoutput2 \ @@ -596,9 +603,15 @@ EXTRA_DIST += \ testdata/testoutput5 \ testdata/testoutput6 \ testdata/testoutput7 \ - testdata/testoutput8-16 \ - testdata/testoutput8-32 \ - testdata/testoutput8-8 \ + testdata/testoutput8-16-2 \ + testdata/testoutput8-16-3 \ + testdata/testoutput8-16-3 \ + testdata/testoutput8-32-2 \ + testdata/testoutput8-32-3 \ + testdata/testoutput8-32-4 \ + testdata/testoutput8-8-2 \ + testdata/testoutput8-8-3 \ + testdata/testoutput8-8-4 \ testdata/testoutput9 \ testdata/testoutput10 \ testdata/testoutput11-16 \ @@ -606,13 +619,22 @@ EXTRA_DIST += \ testdata/testoutput12-16 \ testdata/testoutput12-32 \ testdata/testoutput13 \ - testdata/testoutput14 \ + testdata/testoutput14-16 \ + testdata/testoutput14-32 \ + testdata/testoutput14-8 \ testdata/testoutput15 \ testdata/testoutput16 \ testdata/testoutput17 \ testdata/testoutput18 \ testdata/testoutput19 \ + testdata/testoutput20 \ + testdata/testoutput21 \ + testdata/testoutput22-16 \ + testdata/testoutput22-32 \ + testdata/testoutput22-8 \ + testdata/testoutput23 \ testdata/testoutputEBC \ + testdata/valgrind-jit.supp \ testdata/wintestinput3 \ testdata/wintestoutput3 \ perltest.sh diff --git a/pcre2-10.20/Makefile.in b/pcre2-10.21/Makefile.in similarity index 96% rename from pcre2-10.20/Makefile.in rename to pcre2-10.21/Makefile.in index defe5d688..d86120b5b 100644 --- a/pcre2-10.20/Makefile.in +++ b/pcre2-10.21/Makefile.in @@ -186,7 +186,8 @@ LTLIBRARIES = $(lib_LTLIBRARIES) libpcre2_16_la_DEPENDENCIES = am__libpcre2_16_la_SOURCES_DIST = src/pcre2_auto_possess.c \ src/pcre2_compile.c src/pcre2_config.c src/pcre2_context.c \ - src/pcre2_dfa_match.c src/pcre2_error.c src/pcre2_internal.h \ + src/pcre2_dfa_match.c src/pcre2_error.c \ + src/pcre2_find_bracket.c src/pcre2_internal.h \ src/pcre2_intmodedep.h src/pcre2_jit_compile.c \ src/pcre2_maketables.c src/pcre2_match.c \ src/pcre2_match_data.c src/pcre2_newline.c src/pcre2_ord2utf.c \ @@ -202,6 +203,7 @@ am__objects_1 = src/libpcre2_16_la-pcre2_auto_possess.lo \ src/libpcre2_16_la-pcre2_context.lo \ src/libpcre2_16_la-pcre2_dfa_match.lo \ src/libpcre2_16_la-pcre2_error.lo \ + src/libpcre2_16_la-pcre2_find_bracket.lo \ src/libpcre2_16_la-pcre2_jit_compile.lo \ src/libpcre2_16_la-pcre2_maketables.lo \ src/libpcre2_16_la-pcre2_match.lo \ @@ -235,7 +237,8 @@ libpcre2_16_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ libpcre2_32_la_DEPENDENCIES = am__libpcre2_32_la_SOURCES_DIST = src/pcre2_auto_possess.c \ src/pcre2_compile.c src/pcre2_config.c src/pcre2_context.c \ - src/pcre2_dfa_match.c src/pcre2_error.c src/pcre2_internal.h \ + src/pcre2_dfa_match.c src/pcre2_error.c \ + src/pcre2_find_bracket.c src/pcre2_internal.h \ src/pcre2_intmodedep.h src/pcre2_jit_compile.c \ src/pcre2_maketables.c src/pcre2_match.c \ src/pcre2_match_data.c src/pcre2_newline.c src/pcre2_ord2utf.c \ @@ -250,6 +253,7 @@ am__objects_3 = src/libpcre2_32_la-pcre2_auto_possess.lo \ src/libpcre2_32_la-pcre2_context.lo \ src/libpcre2_32_la-pcre2_dfa_match.lo \ src/libpcre2_32_la-pcre2_error.lo \ + src/libpcre2_32_la-pcre2_find_bracket.lo \ src/libpcre2_32_la-pcre2_jit_compile.lo \ src/libpcre2_32_la-pcre2_maketables.lo \ src/libpcre2_32_la-pcre2_match.lo \ @@ -279,7 +283,8 @@ libpcre2_32_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ libpcre2_8_la_DEPENDENCIES = am__libpcre2_8_la_SOURCES_DIST = src/pcre2_auto_possess.c \ src/pcre2_compile.c src/pcre2_config.c src/pcre2_context.c \ - src/pcre2_dfa_match.c src/pcre2_error.c src/pcre2_internal.h \ + src/pcre2_dfa_match.c src/pcre2_error.c \ + src/pcre2_find_bracket.c src/pcre2_internal.h \ src/pcre2_intmodedep.h src/pcre2_jit_compile.c \ src/pcre2_maketables.c src/pcre2_match.c \ src/pcre2_match_data.c src/pcre2_newline.c src/pcre2_ord2utf.c \ @@ -294,6 +299,7 @@ am__objects_5 = src/libpcre2_8_la-pcre2_auto_possess.lo \ src/libpcre2_8_la-pcre2_context.lo \ src/libpcre2_8_la-pcre2_dfa_match.lo \ src/libpcre2_8_la-pcre2_error.lo \ + src/libpcre2_8_la-pcre2_find_bracket.lo \ src/libpcre2_8_la-pcre2_jit_compile.lo \ src/libpcre2_8_la-pcre2_maketables.lo \ src/libpcre2_8_la-pcre2_match.lo \ @@ -708,7 +714,6 @@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ -MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ @@ -865,6 +870,7 @@ dist_html_DATA = \ doc/html/pcre2_set_character_tables.html \ doc/html/pcre2_set_compile_recursion_guard.html \ doc/html/pcre2_set_match_limit.html \ + doc/html/pcre2_set_offset_limit.html \ doc/html/pcre2_set_newline.html \ doc/html/pcre2_set_parens_nest_limit.html \ doc/html/pcre2_set_recursion_limit.html \ @@ -944,6 +950,7 @@ dist_man_MANS = \ doc/pcre2_set_character_tables.3 \ doc/pcre2_set_compile_recursion_guard.3 \ doc/pcre2_set_match_limit.3 \ + doc/pcre2_set_offset_limit.3 \ doc/pcre2_set_newline.3 \ doc/pcre2_set_parens_nest_limit.3 \ doc/pcre2_set_recursion_limit.3 \ @@ -1052,21 +1059,30 @@ EXTRA_DIST = m4/ax_pthread.m4 m4/pcre2_visibility.m4 \ testdata/testinput11 testdata/testinput12 testdata/testinput13 \ testdata/testinput14 testdata/testinput15 testdata/testinput16 \ testdata/testinput17 testdata/testinput18 testdata/testinput19 \ - testdata/testinputEBC testdata/testoutput1 \ - testdata/testoutput2 testdata/testoutput3 \ + testdata/testinput20 testdata/testinput21 testdata/testinput22 \ + testdata/testinput23 testdata/testinputEBC \ + testdata/testoutput1 testdata/testoutput2 testdata/testoutput3 \ testdata/testoutput3A testdata/testoutput3B \ testdata/testoutput4 testdata/testoutput5 testdata/testoutput6 \ - testdata/testoutput7 testdata/testoutput8-16 \ - testdata/testoutput8-32 testdata/testoutput8-8 \ + testdata/testoutput7 testdata/testoutput8-16-2 \ + testdata/testoutput8-16-3 testdata/testoutput8-16-3 \ + testdata/testoutput8-32-2 testdata/testoutput8-32-3 \ + testdata/testoutput8-32-4 testdata/testoutput8-8-2 \ + testdata/testoutput8-8-3 testdata/testoutput8-8-4 \ testdata/testoutput9 testdata/testoutput10 \ testdata/testoutput11-16 testdata/testoutput11-32 \ testdata/testoutput12-16 testdata/testoutput12-32 \ - testdata/testoutput13 testdata/testoutput14 \ + testdata/testoutput13 testdata/testoutput14-16 \ + testdata/testoutput14-32 testdata/testoutput14-8 \ testdata/testoutput15 testdata/testoutput16 \ testdata/testoutput17 testdata/testoutput18 \ - testdata/testoutput19 testdata/testoutputEBC \ - testdata/wintestinput3 testdata/wintestoutput3 perltest.sh \ - src/pcre2demo.c cmake/COPYING-CMAKE-SCRIPTS \ + testdata/testoutput19 testdata/testoutput20 \ + testdata/testoutput21 testdata/testoutput22-16 \ + testdata/testoutput22-32 testdata/testoutput22-8 \ + testdata/testoutput23 testdata/testoutputEBC \ + testdata/valgrind-jit.supp testdata/wintestinput3 \ + testdata/wintestoutput3 perltest.sh src/pcre2demo.c \ + cmake/COPYING-CMAKE-SCRIPTS \ cmake/FindPackageHandleStandardArgs.cmake \ cmake/FindReadline.cmake cmake/FindEditline.cmake \ CMakeLists.txt config-cmake.h.in @@ -1088,6 +1104,7 @@ COMMON_SOURCES = \ src/pcre2_context.c \ src/pcre2_dfa_match.c \ src/pcre2_error.c \ + src/pcre2_find_bracket.c \ src/pcre2_internal.h \ src/pcre2_intmodedep.h \ src/pcre2_jit_compile.c \ @@ -1198,7 +1215,7 @@ all: $(BUILT_SOURCES) .SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs am--refresh: Makefile @: -$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ @@ -1224,9 +1241,9 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) $(SHELL) ./config.status --recheck -$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) +$(top_srcdir)/configure: $(am__configure_deps) $(am__cd) $(srcdir) && $(AUTOCONF) -$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) +$(ACLOCAL_M4): $(am__aclocal_m4_deps) $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) $(am__aclocal_m4_deps): @@ -1237,7 +1254,7 @@ src/config.h: src/stamp-h1 src/stamp-h1: $(top_srcdir)/src/config.h.in $(top_builddir)/config.status @rm -f src/stamp-h1 cd $(top_builddir) && $(SHELL) ./config.status src/config.h -$(top_srcdir)/src/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) +$(top_srcdir)/src/config.h.in: $(am__configure_deps) ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) rm -f src/stamp-h1 touch $@ @@ -1309,6 +1326,8 @@ src/libpcre2_16_la-pcre2_dfa_match.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libpcre2_16_la-pcre2_error.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) +src/libpcre2_16_la-pcre2_find_bracket.lo: src/$(am__dirstamp) \ + src/$(DEPDIR)/$(am__dirstamp) src/libpcre2_16_la-pcre2_jit_compile.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libpcre2_16_la-pcre2_maketables.lo: src/$(am__dirstamp) \ @@ -1358,6 +1377,8 @@ src/libpcre2_32_la-pcre2_dfa_match.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libpcre2_32_la-pcre2_error.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) +src/libpcre2_32_la-pcre2_find_bracket.lo: src/$(am__dirstamp) \ + src/$(DEPDIR)/$(am__dirstamp) src/libpcre2_32_la-pcre2_jit_compile.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libpcre2_32_la-pcre2_maketables.lo: src/$(am__dirstamp) \ @@ -1407,6 +1428,8 @@ src/libpcre2_8_la-pcre2_dfa_match.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libpcre2_8_la-pcre2_error.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) +src/libpcre2_8_la-pcre2_find_bracket.lo: src/$(am__dirstamp) \ + src/$(DEPDIR)/$(am__dirstamp) src/libpcre2_8_la-pcre2_jit_compile.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libpcre2_8_la-pcre2_maketables.lo: src/$(am__dirstamp) \ @@ -1583,6 +1606,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_16_la-pcre2_context.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_16_la-pcre2_dfa_match.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_16_la-pcre2_error.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_16_la-pcre2_find_bracket.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_16_la-pcre2_jit_compile.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_16_la-pcre2_maketables.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_16_la-pcre2_match.Plo@am__quote@ @@ -1606,6 +1630,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_32_la-pcre2_context.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_32_la-pcre2_dfa_match.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_32_la-pcre2_error.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_32_la-pcre2_find_bracket.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_32_la-pcre2_jit_compile.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_32_la-pcre2_maketables.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_32_la-pcre2_match.Plo@am__quote@ @@ -1629,6 +1654,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_8_la-pcre2_context.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_8_la-pcre2_dfa_match.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_8_la-pcre2_error.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_8_la-pcre2_find_bracket.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_8_la-pcre2_jit_compile.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_8_la-pcre2_maketables.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_8_la-pcre2_match.Plo@am__quote@ @@ -1716,6 +1742,13 @@ src/libpcre2_16_la-pcre2_error.lo: src/pcre2_error.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libpcre2_16_la_CFLAGS) $(CFLAGS) -c -o src/libpcre2_16_la-pcre2_error.lo `test -f 'src/pcre2_error.c' || echo '$(srcdir)/'`src/pcre2_error.c +src/libpcre2_16_la-pcre2_find_bracket.lo: src/pcre2_find_bracket.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libpcre2_16_la_CFLAGS) $(CFLAGS) -MT src/libpcre2_16_la-pcre2_find_bracket.lo -MD -MP -MF src/$(DEPDIR)/libpcre2_16_la-pcre2_find_bracket.Tpo -c -o src/libpcre2_16_la-pcre2_find_bracket.lo `test -f 'src/pcre2_find_bracket.c' || echo '$(srcdir)/'`src/pcre2_find_bracket.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libpcre2_16_la-pcre2_find_bracket.Tpo src/$(DEPDIR)/libpcre2_16_la-pcre2_find_bracket.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/pcre2_find_bracket.c' object='src/libpcre2_16_la-pcre2_find_bracket.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libpcre2_16_la_CFLAGS) $(CFLAGS) -c -o src/libpcre2_16_la-pcre2_find_bracket.lo `test -f 'src/pcre2_find_bracket.c' || echo '$(srcdir)/'`src/pcre2_find_bracket.c + src/libpcre2_16_la-pcre2_jit_compile.lo: src/pcre2_jit_compile.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libpcre2_16_la_CFLAGS) $(CFLAGS) -MT src/libpcre2_16_la-pcre2_jit_compile.lo -MD -MP -MF src/$(DEPDIR)/libpcre2_16_la-pcre2_jit_compile.Tpo -c -o src/libpcre2_16_la-pcre2_jit_compile.lo `test -f 'src/pcre2_jit_compile.c' || echo '$(srcdir)/'`src/pcre2_jit_compile.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libpcre2_16_la-pcre2_jit_compile.Tpo src/$(DEPDIR)/libpcre2_16_la-pcre2_jit_compile.Plo @@ -1877,6 +1910,13 @@ src/libpcre2_32_la-pcre2_error.lo: src/pcre2_error.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libpcre2_32_la_CFLAGS) $(CFLAGS) -c -o src/libpcre2_32_la-pcre2_error.lo `test -f 'src/pcre2_error.c' || echo '$(srcdir)/'`src/pcre2_error.c +src/libpcre2_32_la-pcre2_find_bracket.lo: src/pcre2_find_bracket.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libpcre2_32_la_CFLAGS) $(CFLAGS) -MT src/libpcre2_32_la-pcre2_find_bracket.lo -MD -MP -MF src/$(DEPDIR)/libpcre2_32_la-pcre2_find_bracket.Tpo -c -o src/libpcre2_32_la-pcre2_find_bracket.lo `test -f 'src/pcre2_find_bracket.c' || echo '$(srcdir)/'`src/pcre2_find_bracket.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libpcre2_32_la-pcre2_find_bracket.Tpo src/$(DEPDIR)/libpcre2_32_la-pcre2_find_bracket.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/pcre2_find_bracket.c' object='src/libpcre2_32_la-pcre2_find_bracket.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libpcre2_32_la_CFLAGS) $(CFLAGS) -c -o src/libpcre2_32_la-pcre2_find_bracket.lo `test -f 'src/pcre2_find_bracket.c' || echo '$(srcdir)/'`src/pcre2_find_bracket.c + src/libpcre2_32_la-pcre2_jit_compile.lo: src/pcre2_jit_compile.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libpcre2_32_la_CFLAGS) $(CFLAGS) -MT src/libpcre2_32_la-pcre2_jit_compile.lo -MD -MP -MF src/$(DEPDIR)/libpcre2_32_la-pcre2_jit_compile.Tpo -c -o src/libpcre2_32_la-pcre2_jit_compile.lo `test -f 'src/pcre2_jit_compile.c' || echo '$(srcdir)/'`src/pcre2_jit_compile.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libpcre2_32_la-pcre2_jit_compile.Tpo src/$(DEPDIR)/libpcre2_32_la-pcre2_jit_compile.Plo @@ -2038,6 +2078,13 @@ src/libpcre2_8_la-pcre2_error.lo: src/pcre2_error.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libpcre2_8_la_CFLAGS) $(CFLAGS) -c -o src/libpcre2_8_la-pcre2_error.lo `test -f 'src/pcre2_error.c' || echo '$(srcdir)/'`src/pcre2_error.c +src/libpcre2_8_la-pcre2_find_bracket.lo: src/pcre2_find_bracket.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libpcre2_8_la_CFLAGS) $(CFLAGS) -MT src/libpcre2_8_la-pcre2_find_bracket.lo -MD -MP -MF src/$(DEPDIR)/libpcre2_8_la-pcre2_find_bracket.Tpo -c -o src/libpcre2_8_la-pcre2_find_bracket.lo `test -f 'src/pcre2_find_bracket.c' || echo '$(srcdir)/'`src/pcre2_find_bracket.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libpcre2_8_la-pcre2_find_bracket.Tpo src/$(DEPDIR)/libpcre2_8_la-pcre2_find_bracket.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/pcre2_find_bracket.c' object='src/libpcre2_8_la-pcre2_find_bracket.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libpcre2_8_la_CFLAGS) $(CFLAGS) -c -o src/libpcre2_8_la-pcre2_find_bracket.lo `test -f 'src/pcre2_find_bracket.c' || echo '$(srcdir)/'`src/pcre2_find_bracket.c + src/libpcre2_8_la-pcre2_jit_compile.lo: src/pcre2_jit_compile.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libpcre2_8_la_CFLAGS) $(CFLAGS) -MT src/libpcre2_8_la-pcre2_jit_compile.lo -MD -MP -MF src/$(DEPDIR)/libpcre2_8_la-pcre2_jit_compile.Tpo -c -o src/libpcre2_8_la-pcre2_jit_compile.lo `test -f 'src/pcre2_jit_compile.c' || echo '$(srcdir)/'`src/pcre2_jit_compile.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libpcre2_8_la-pcre2_jit_compile.Tpo src/$(DEPDIR)/libpcre2_8_la-pcre2_jit_compile.Plo diff --git a/pcre2-10.21/NEWS b/pcre2-10.21/NEWS new file mode 100644 index 000000000..aaeee5c24 --- /dev/null +++ b/pcre2-10.21/NEWS @@ -0,0 +1,88 @@ +News about PCRE2 releases +------------------------- + +Version 10.21 12-January-2016 +----------------------------- + +1. Many bugs have been fixed. A large number of them were provoked only by very +strange pattern input, and were discovered by fuzzers. Some others were +discovered by code auditing. See ChangeLog for details. + +2. The Unicode tables have been updated to Unicode version 8.0.0. + +3. For Perl compatibility in EBCDIC environments, ranges such as a-z in a +class, where both values are literal letters in the same case, omit the +non-letter EBCDIC code points within the range. + +4. There have been a number of enhancements to the pcre2_substitute() function, +giving more flexibility to replacement facilities. It is now also possible to +cause the function to return the needed buffer size if the one given is too +small. + +5. The PCRE2_ALT_VERBNAMES option causes the "name" parts of special verbs such +as (*THEN:name) to be processed for backslashes and to take note of +PCRE2_EXTENDED. + +6. PCRE2_INFO_HASBACKSLASHC makes it possible for a client to find out if a +pattern uses \C, and --never-backslash-C makes it possible to compile a version +PCRE2 in which the use of \C is always forbidden. + +7. A limit to the length of pattern that can be handled can now be set by +calling pcre2_set_max_pattern_length(). + +8. When matching an unanchored pattern, a match can be required to begin within +a given number of code units after the start of the subject by calling +pcre2_set_offset_limit(). + +9. The pcre2test program has been extended to test new facilities, and it can +now run the tests when LF on its own is not a valid newline sequence. + +10. The RunTest script has also been updated to enable more tests to be run. + +11. There have been some minor performance enhancements. + + +Version 10.20 30-June-2015 +-------------------------- + +1. Callouts with string arguments and the pcre2_callout_enumerate() function +have been implemented. + +2. The PCRE2_NEVER_BACKSLASH_C option, which locks out the use of \C, is added. + +3. The PCRE2_ALT_CIRCUMFLEX option lets ^ match after a newline at the end of a +subject in multiline mode. + +4. The way named subpatterns are handled has been refactored. The previous +approach had several bugs. + +5. The handling of \c in EBCDIC environments has been changed to conform to the +perlebcdic document. This is an incompatible change. + +6. Bugs have been mended, many of them discovered by fuzzers. + + +Version 10.10 06-March-2015 +--------------------------- + +1. Serialization and de-serialization functions have been added to the API, +making it possible to save and restore sets of compiled patterns, though +restoration must be done in the same environment that was used for compilation. + +2. The (*NO_JIT) feature has been added; this makes it possible for a pattern +creator to specify that JIT is not to be used. + +3. A number of bugs have been fixed. In particular, bugs that caused building +on Windows using CMake to fail have been mended. + + +Version 10.00 05-January-2015 +----------------------------- + +Version 10.00 is the first release of PCRE2, a revised API for the PCRE +library. Changes prior to 10.00 are logged in the ChangeLog file for the old +API, up to item 20 for release 8.36. New programs are recommended to use the +new library. Programs that use the original (PCRE1) API will need changing +before linking with the new library. + +**** diff --git a/pcre2-10.20/NON-AUTOTOOLS-BUILD b/pcre2-10.21/NON-AUTOTOOLS-BUILD similarity index 99% rename from pcre2-10.20/NON-AUTOTOOLS-BUILD rename to pcre2-10.21/NON-AUTOTOOLS-BUILD index d8d9d2b49..ceb92450c 100644 --- a/pcre2-10.20/NON-AUTOTOOLS-BUILD +++ b/pcre2-10.21/NON-AUTOTOOLS-BUILD @@ -97,6 +97,7 @@ can skip ahead to the CMake section. pcre2_context.c pcre2_dfa_match.c pcre2_error.c + pcre2_find_bracket.c pcre2_jit_compile.c pcre2_maketables.c pcre2_match.c @@ -388,4 +389,4 @@ and executable, is in EBCDIC and native z/OS file formats and this is the recommended download site. ============================= -Last Updated: 15 June 2015 +Last Updated: 16 July 2015 diff --git a/pcre2-10.20/PrepareRelease b/pcre2-10.21/PrepareRelease similarity index 96% rename from pcre2-10.20/PrepareRelease rename to pcre2-10.21/PrepareRelease index f6b138f87..114fce01d 100755 --- a/pcre2-10.20/PrepareRelease +++ b/pcre2-10.21/PrepareRelease @@ -65,13 +65,9 @@ End echo "Making pcre2.txt" for file in pcre2 pcre2api pcre2build pcre2callout pcre2compat pcre2jit \ - pcre2limits pcre2matching pcre2partial pcre2unicode ; do - -#for file in \ -# pcre2syntax \ -# pcre2precompile pcre2perform pcre2posix pcre2sample \ -# pcre2stack ; do - + pcre2limits pcre2matching pcre2partial pcre2pattern pcre2perform \ + pcre2posix pcre2sample pcre2serialize pcre2stack pcre2syntax \ + pcre2unicode ; do echo " Processing $file.3" nroff -c -man $file.3 >$file.rawtxt perl ../CleanTxt <$file.rawtxt >>pcre2.txt @@ -153,7 +149,6 @@ for file in *.3 ; do [ "$base" = "pcre2stack" ] || \ [ "$base" = "pcre2compat" ] || \ [ "$base" = "pcre2limits" ] || \ - [ "$base" = "pcre2perform" ] || \ [ "$base" = "pcre2unicode" ] ; then toc="" fi @@ -204,6 +199,7 @@ files="\ src/pcre2_context.c \ src/pcre2_dfa_match.c \ src/pcre2_error.c \ + src/pcre2_find_bracket.c \ src/pcre2_internal.h \ src/pcre2_intmodedep.h \ src/pcre2_jit_compile.c \ diff --git a/pcre2-10.20/README b/pcre2-10.21/README similarity index 98% rename from pcre2-10.20/README rename to pcre2-10.21/README index 7367924c8..48d2ffdd7 100644 --- a/pcre2-10.20/README +++ b/pcre2-10.21/README @@ -220,6 +220,13 @@ library. They are also documented in the pcre2build man page. restrict \R to match only CR, LF, or CRLF. You can make this the default by adding --enable-bsr-anycrlf to the "configure" command (bsr = "backslash R"). +. In a pattern, the escape sequence \C matches a single code unit, even in a + UTF mode. This can be dangerous because it breaks up multi-code-unit + characters. You can build PCRE2 with the use of \C permanently locked out by + adding --enable-never-backslash-C (note the upper case C) to the "configure" + command. When \C is allowed by the library, individual applications can lock + it out by calling pcre2_compile() with the PCRE2_NEVER_BACKSLASH_C option. + . PCRE2 has a counter that limits the depth of nesting of parentheses in a pattern. This limits the amount of system stack that a pattern uses when it is compiled. The default is 250, but you can change it by setting, for @@ -724,6 +731,7 @@ The distribution should contain the files listed below. src/pcre2_context.c ) src/pcre2_dfa_match.c ) src/pcre2_error.c ) + src/pcre2_find_bracket.c ) src/pcre2_jit_compile.c ) src/pcre2_jit_match.c ) sources for the functions in the library, src/pcre2_jit_misc.c ) and some internal functions that they use @@ -832,4 +840,4 @@ The distribution should contain the files listed below. Philip Hazel Email local part: ph10 Email domain: cam.ac.uk -Last updated: 24 April 2015 +Last updated: 16 October 2015 diff --git a/pcre2-10.20/RunGrepTest b/pcre2-10.21/RunGrepTest similarity index 97% rename from pcre2-10.20/RunGrepTest rename to pcre2-10.21/RunGrepTest index f7db29e6c..67d672ba3 100755 --- a/pcre2-10.20/RunGrepTest +++ b/pcre2-10.21/RunGrepTest @@ -19,12 +19,18 @@ unset cp ls mv rm builddir=`pwd` pcre2grep=$builddir/pcre2grep +pcre2test=$builddir/pcre2test if [ ! -x $pcre2grep ] ; then echo "** $pcre2grep does not exist or is not execuatble." exit 1 fi +if [ ! -x $pcre2test ] ; then + echo "** $pcre2test does not exist or is not execuatble." + exit 1 +fi + valgrind= while [ $# -gt 0 ] ; do case $1 in @@ -34,7 +40,6 @@ while [ $# -gt 0 ] ; do shift done -echo " " pcre2grep_version=`$pcre2grep -V` if [ "$valgrind" = "" ] ; then echo "Testing $pcre2grep_version" @@ -69,14 +74,22 @@ fi # Check for the availability of UTF-8 support -./pcre2test -C unicode >/dev/null +$pcre2test -C unicode >/dev/null utf8=$? +# Check default newline convention. If it does not include LF, force LF. + +nl=`$pcre2test -C newline` +if [ "$nl" != "LF" -a "$nl" != "ANY" -a "$nl" != "ANYCRLF" ]; then + pcre2grep="$pcre2grep -N LF" + echo "Default newline setting forced to LF" +fi + # ------ Function to run and check a special pcre2grep arguments test ------- checkspecial() { - $valgrind ./pcre2grep $1 >>testtrygrep 2>&1 + $valgrind $pcre2grep $1 >>testtrygrep 2>&1 if [ $? -ne $2 ] ; then echo "** pcre2grep $1 failed - check testtrygrep" exit 1 @@ -530,6 +543,14 @@ echo "aaaaa" >>testtemp1grep (cd $srcdir; $valgrind $pcre2grep --line-offsets '(?<=\Ka)' $builddir/testtemp1grep) >>testtrygrep 2>&1 echo "RC=$?" >>testtrygrep +echo "---------------------------- Test 108 ------------------------------" >>testtrygrep +(cd $srcdir; $valgrind $pcre2grep -lq PATTERN ./testdata/grepinput ./testdata/grepinputx) >>testtrygrep +echo "RC=$?" >>testtrygrep + +echo "---------------------------- Test 109 -----------------------------" >>testtrygrep +(cd $srcdir; $valgrind $pcre2grep -cq lazy ./testdata/grepinput*) >>testtrygrep +echo "RC=$?" >>testtrygrep + # Now compare the results. $cf $srcdir/testdata/grepoutput testtrygrep diff --git a/pcre2-10.20/RunTest b/pcre2-10.21/RunTest similarity index 76% rename from pcre2-10.20/RunTest rename to pcre2-10.21/RunTest index c4d659c76..36dc638ed 100755 --- a/pcre2-10.20/RunTest +++ b/pcre2-10.21/RunTest @@ -33,6 +33,10 @@ # For backwards compatibility, -nojit, -valgrind, -valgrind-log, and -sim may # be given without the leading "-" character. # +# When PCRE2 is compiled by clang with -fsanitize arguments, some tests need +# very much more stack than normal. In environments where the stack can be +# set at runtime, -bigstack sets a gigantic stack. +# # There are two special cases where only one argument is allowed: # # If the first and only argument is "ebcdic", the script runs the special @@ -64,13 +68,17 @@ title10="Test 10: Specials for the 8-bit library with UTF-8 and UCP support" title11="Test 11: Specials for the basic 16-bit and 32-bit libraries" title12="Test 12: Specials for the 16-bit and 32-bit libraries UTF and UCP support" title13="Test 13: DFA specials for the basic 16-bit and 32-bit libraries" -title14="Test 14: Non-JIT limits and other non-JIT tests" -title15="Test 15: JIT-specific features when JIT is not available" -title16="Test 16: JIT-specific features when JIT is available" -title17="Test 17: Tests of the POSIX interface, excluding UTF/UCP" -title18="Test 18: Tests of the POSIX interface with UTF/UCP" -title19="Test 19: Serialization tests" -maxtest=18 +title14="Test 14: DFA specials for UTF and UCP support" +title15="Test 15: Non-JIT limits and other non-JIT tests" +title16="Test 16: JIT-specific features when JIT is not available" +title17="Test 17: JIT-specific features when JIT is available" +title18="Test 18: Tests of the POSIX interface, excluding UTF/UCP" +title19="Test 19: Tests of the POSIX interface with UTF/UCP" +title20="Test 20: Serialization tests" +title21="Test 21: \C tests without UTF (supported for DFA matching)" +title22="Test 22: \C tests with UTF (not supported for DFA matching)" +title23="Test 23: \C disabled test" +maxtest=23 if [ $# -eq 1 -a "$1" = "list" ]; then echo $title0 @@ -93,6 +101,10 @@ if [ $# -eq 1 -a "$1" = "list" ]; then echo $title17 echo $title18 echo $title19 + echo $title20 + echo $title21 + echo $title22 + echo $title23 exit 0 fi @@ -151,7 +163,7 @@ checkresult() checkspecial() { - $valgrind ./pcre2test $1 >>testtry + $valgrind $vjs ./pcre2test $1 >>testtry if [ $? -ne 0 ] ; then echo "** pcre2test $1 failed - check testtry" exit 1 @@ -184,9 +196,11 @@ arg8= arg16= arg32= nojit= +bigstack= sim= skip= valgrind= +vjs= # This is in case the caller has set aliases (as I do - PH) unset cp ls mv rm @@ -214,6 +228,10 @@ do16=no do17=no do18=no do19=no +do20=no +do21=no +do22=no +do23=no while [ $# -gt 0 ] ; do case $1 in @@ -237,9 +255,14 @@ while [ $# -gt 0 ] ; do 17) do17=yes;; 18) do18=yes;; 19) do19=yes;; + 20) do20=yes;; + 21) do21=yes;; + 22) do22=yes;; + 23) do23=yes;; -8) arg8=yes;; -16) arg16=yes;; -32) arg32=yes;; + bigstack|-bigstack) bigstack=yes;; nojit|-nojit) nojit=yes;; sim|-sim) shift; sim=$1;; valgrind|-valgrind) valgrind="valgrind --tool=memcheck -q --smc-check=all";; @@ -287,13 +310,22 @@ fi # If it is possible to set the system stack size, arrange to set a value for # test 2, which needs more than the even the Linux default when PCRE2 has been -# compiled with -fsanitize=address. +# compiled by gcc with -fsanitize=address. When the compiler is clang, sanitize +# options require an even bigger stack for test 2, and an increased stack for +# some of the other tests. $sim ./pcre2test -S 1 /dev/null /dev/null if [ $? -eq 0 ] ; then - test2stack="-S 16" + if [ "$bigstack" = "" ] ; then + test2stack="-S 16" + defaultstack="" + else + test2stack="-S 1024" + defaultstack="-S 64" + fi else test2stack="" + defaultstack="" fi # All of 8-bit, 16-bit, and 32-bit character strings may be supported, but only @@ -306,6 +338,11 @@ support16=$? $sim ./pcre2test -C pcre2-32 >/dev/null support32=$? +# \C may be disabled + +$sim ./pcre2test -C backslash-C >/dev/null +supportBSC=$? + # Initialize all bitsizes skipped test8=skip @@ -358,11 +395,18 @@ fi $sim ./pcre2test -C unicode >/dev/null utf=$? +# When JIT is used with valgrind, we need to set up valgrind suppressions as +# otherwise there are a lot of false positive valgrind reports when the +# the hardware supports SSE2. + jitopt= $sim ./pcre2test -C jit >/dev/null jit=$? if [ $jit -ne 0 -a "$nojit" != "yes" ] ; then jitopt=-jit + if [ "$valgrind" != "" ] ; then + vjs="--suppressions=$testdata/valgrind-jit.supp" + fi fi # If no specific tests were requested, select all. Those that are not @@ -372,7 +416,8 @@ if [ $do0 = no -a $do1 = no -a $do2 = no -a $do3 = no -a \ $do4 = no -a $do5 = no -a $do6 = no -a $do7 = no -a \ $do8 = no -a $do9 = no -a $do10 = no -a $do11 = no -a \ $do12 = no -a $do13 = no -a $do14 = no -a $do15 = no -a \ - $do16 = no -a $do17 = no -a $do18 = no -a $do19 = no \ + $do16 = no -a $do17 = no -a $do18 = no -a $do19 = no -a \ + $do20 = no -a $do21 = no -a $do22 = no -a $do23 = no \ ]; then do0=yes do1=yes @@ -394,6 +439,10 @@ if [ $do0 = no -a $do1 = no -a $do2 = no -a $do3 = no -a \ do17=yes do18=yes do19=yes + do20=yes + do21=yes + do22=yes + do23=yes fi # Handle any explicit skips at this stage, so that an argument list may consist @@ -438,7 +487,7 @@ for bmode in "$test8" "$test16" "$test32"; do if [ $do1 = yes ] ; then echo $title1 for opt in "" $jitopt; do - $sim $valgrind ./pcre2test -q $bmode $opt $testdata/testinput1 testtry + $sim $valgrind ${opt:+$vjs} ./pcre2test -q $defaultstack $bmode $opt $testdata/testinput1 testtry checkresult $? 1 "$opt" done fi @@ -448,7 +497,7 @@ for bmode in "$test8" "$test16" "$test32"; do if [ $do2 = yes ] ; then echo $title2 "(excluding UTF-$bits)" for opt in "" $jitopt; do - $sim $valgrind ./pcre2test -q $test2stack $bmode $opt $testdata/testinput2 testtry + $sim $valgrind ${opt:+$vjs} ./pcre2test -q $test2stack $bmode $opt $testdata/testinput2 testtry if [ $? = 0 ] ; then checkresult $? 2 "$opt" else @@ -508,7 +557,7 @@ for bmode in "$test8" "$test16" "$test32"; do if [ "$locale" != "" ] ; then echo $title3 "(using '$locale' locale)" for opt in "" $jitopt; do - $sim $valgrind ./pcre2test -q $bmode $opt $infile testtry + $sim $valgrind ${opt:+$vjs} ./pcre2test -q $defaultstack $bmode $opt $infile testtry if [ $? = 0 ] ; then case "$opt" in -jit) with=" with JIT";; @@ -545,7 +594,7 @@ for bmode in "$test8" "$test16" "$test32"; do echo " Skipped because UTF-$bits support is not available" else for opt in "" $jitopt; do - $sim $valgrind ./pcre2test -q $bmode $opt $testdata/testinput4 testtry + $sim $valgrind ${opt:+$vjs} ./pcre2test -q $defaultstack $bmode $opt $testdata/testinput4 testtry checkresult $? 4 "$opt" done fi @@ -557,7 +606,7 @@ for bmode in "$test8" "$test16" "$test32"; do echo " Skipped because UTF-$bits support is not available" else for opt in "" $jitopt; do - $sim $valgrind ./pcre2test -q $bmode $opt $testdata/testinput5 testtry + $sim $valgrind ${opt:+$vjs} ./pcre2test -q $defaultstack $bmode $opt $testdata/testinput5 testtry checkresult $? 5 "$opt" done fi @@ -567,7 +616,7 @@ for bmode in "$test8" "$test16" "$test32"; do if [ $do6 = yes ] ; then echo $title6 - $sim $valgrind ./pcre2test -q $bmode $testdata/testinput6 testtry + $sim $valgrind ./pcre2test -q $defaultstack $bmode $testdata/testinput6 testtry checkresult $? 6 "" fi @@ -576,28 +625,26 @@ for bmode in "$test8" "$test16" "$test32"; do if [ $utf -eq 0 ] ; then echo " Skipped because UTF-$bits support is not available" else - $sim $valgrind ./pcre2test -q $bmode $opt $testdata/testinput7 testtry + $sim $valgrind ./pcre2test -q $defaultstack $bmode $opt $testdata/testinput7 testtry checkresult $? 7 "" fi fi # Test of internal offsets and code sizes. This test is run only when there - # is UTF/UCP support and the link size is 2. The actual tests are - # mostly the same as in some of the above, but in this test we inspect some - # offsets and sizes that require a known link size. This is a doublecheck for - # the maintainer, just in case something changes unexpectely. The output from - # this test is different in 8-bit, 16-bit, and 32-bit modes, so there are - # mode-specific output files. + # is UTF/UCP support. The actual tests are mostly the same as in some of the + # above, but in this test we inspect some offsets and sizes. This is a + # doublecheck for the maintainer, just in case something changes unexpectely. + # The output from this test is different in 8-bit, 16-bit, and 32-bit modes + # and for different link sizes, so there are different output files for each + # mode and link size. if [ $do8 = yes ] ; then echo $title8 - if [ $link_size -ne 2 ] ; then - echo " Skipped because link size is not 2" - elif [ $utf -eq 0 ] ; then + if [ $utf -eq 0 ] ; then echo " Skipped because UTF-$bits support is not available" else - $sim $valgrind ./pcre2test -q $bmode $testdata/testinput8 testtry - checkresult $? 8-$bits "" + $sim $valgrind ./pcre2test -q $defaultstack $bmode $testdata/testinput8 testtry + checkresult $? 8-$bits-$link_size "" fi fi @@ -609,7 +656,7 @@ for bmode in "$test8" "$test16" "$test32"; do echo " Skipped when running 16/32-bit tests" else for opt in "" $jitopt; do - $sim $valgrind ./pcre2test -q $bmode $opt $testdata/testinput9 testtry + $sim $valgrind ${opt:+$vjs} ./pcre2test -q $defaultstack $bmode $opt $testdata/testinput9 testtry checkresult $? 9 "$opt" done fi @@ -625,7 +672,7 @@ for bmode in "$test8" "$test16" "$test32"; do echo " Skipped because UTF-$bits support is not available" else for opt in "" $jitopt; do - $sim $valgrind ./pcre2test -q $bmode $opt $testdata/testinput10 testtry + $sim $valgrind ${opt:+$vjs} ./pcre2test -q $defaultstack $bmode $opt $testdata/testinput10 testtry checkresult $? 10 "$opt" done fi @@ -639,7 +686,7 @@ for bmode in "$test8" "$test16" "$test32"; do echo " Skipped when running 8-bit tests" else for opt in "" $jitopt; do - $sim $valgrind ./pcre2test -q $bmode $opt $testdata/testinput11 testtry + $sim $valgrind ${opt:+$vjs} ./pcre2test -q $defaultstack $bmode $opt $testdata/testinput11 testtry checkresult $? 11-$bits "$opt" done fi @@ -656,7 +703,7 @@ for bmode in "$test8" "$test16" "$test32"; do echo " Skipped because UTF-$bits support is not available" else for opt in "" $jitopt; do - $sim $valgrind ./pcre2test -q $bmode $opt $testdata/testinput12 testtry + $sim $valgrind ${opt:+$vjs} ./pcre2test -q $defaultstack $bmode $opt $testdata/testinput12 testtry checkresult $? 12-$bits "$opt" done fi @@ -669,75 +716,129 @@ for bmode in "$test8" "$test16" "$test32"; do if [ "$bits" = "8" ] ; then echo " Skipped when running 8-bit tests" else - $sim $valgrind ./pcre2test -q $bmode $testdata/testinput13 testtry + $sim $valgrind ./pcre2test -q $defaultstack $bmode $testdata/testinput13 testtry checkresult $? 13 "" fi fi + # Tests for DFA UTF and UCP features. Output is different for the different widths. + + if [ $do14 = yes ] ; then + echo $title14 + if [ $utf -eq 0 ] ; then + echo " Skipped because UTF-$bits support is not available" + else + $sim $valgrind ./pcre2test -q $defaultstack $bmode $opt $testdata/testinput14 testtry + checkresult $? 14-$bits "" + fi + fi + # Test non-JIT match and recursion limits - if [ $do14 = yes ] ; then - echo $title14 - $sim $valgrind ./pcre2test -q $bmode $testdata/testinput14 testtry - checkresult $? 14 "" + if [ $do15 = yes ] ; then + echo $title15 + $sim $valgrind ./pcre2test -q $defaultstack $bmode $testdata/testinput15 testtry + checkresult $? 15 "" fi # Test JIT-specific features when JIT is not available - if [ $do15 = yes ] ; then - echo $title15 + if [ $do16 = yes ] ; then + echo $title16 if [ $jit -ne 0 ] ; then echo " Skipped because JIT is available" else - $sim $valgrind ./pcre2test -q $bmode $testdata/testinput15 testtry - checkresult $? 15 "" + $sim $valgrind ./pcre2test -q $defaultstack $bmode $testdata/testinput16 testtry + checkresult $? 16 "" fi fi # Test JIT-specific features when JIT is available - if [ $do16 = yes ] ; then - echo $title16 + if [ $do17 = yes ] ; then + echo $title17 if [ $jit -eq 0 -o "$nojit" = "yes" ] ; then echo " Skipped because JIT is not available or nojit was specified" else - $sim $valgrind ./pcre2test -q $bmode $testdata/testinput16 testtry - checkresult $? 16 "" + $sim $valgrind $vjs ./pcre2test -q $defaultstack $bmode $testdata/testinput17 testtry + checkresult $? 17 "" fi fi # Tests for the POSIX interface without UTF/UCP (8-bit only) - if [ $do17 = yes ] ; then - echo $title17 + if [ $do18 = yes ] ; then + echo $title18 if [ "$bits" = "16" -o "$bits" = "32" ] ; then echo " Skipped when running 16/32-bit tests" else - $sim $valgrind ./pcre2test -q $bmode $testdata/testinput17 testtry - checkresult $? 17 "" + $sim $valgrind ./pcre2test -q $defaultstack $bmode $testdata/testinput18 testtry + checkresult $? 18 "" fi fi # Tests for the POSIX interface with UTF/UCP (8-bit only) - if [ $do18 = yes ] ; then - echo $title18 + if [ $do19 = yes ] ; then + echo $title19 if [ "$bits" = "16" -o "$bits" = "32" ] ; then echo " Skipped when running 16/32-bit tests" elif [ $utf -eq 0 ] ; then echo " Skipped because UTF-$bits support is not available" else - $sim $valgrind ./pcre2test -q $bmode $testdata/testinput18 testtry - checkresult $? 18 "" + $sim $valgrind ./pcre2test -q $defaultstack $bmode $testdata/testinput19 testtry + checkresult $? 19 "" fi fi # Serialization tests - if [ $do19 = yes ] ; then - echo $title19 - $sim $valgrind ./pcre2test -q $bmode $testdata/testinput19 testtry - checkresult $? 19 "" + if [ $do20 = yes ] ; then + echo $title20 + $sim $valgrind ./pcre2test -q $defaultstack $bmode $testdata/testinput20 testtry + checkresult $? 20 "" + fi + + # \C tests without UTF - DFA matching is supported + + if [ "$do21" = yes ] ; then + echo $title21 + if [ $supportBSC -eq 0 ] ; then + echo " Skipped because \C is disabled" + else + for opt in "" $jitopt -dfa; do + $sim $valgrind ${opt:+$vjs} ./pcre2test -q $defaultstack $bmode $opt $testdata/testinput21 testtry + checkresult $? 21 "$opt" + done + fi + fi + + # \C tests with UTF - DFA matching is not supported for \C in UTF mode + + if [ "$do22" = yes ] ; then + echo $title22 + if [ $supportBSC -eq 0 ] ; then + echo " Skipped because \C is disabled" + elif [ $utf -eq 0 ] ; then + echo " Skipped because UTF-$bits support is not available" + else + for opt in "" $jitopt; do + $sim $valgrind ${opt:+$vjs} ./pcre2test -q $defaultstack $bmode $opt $testdata/testinput22 testtry + checkresult $? 22-$bits "$opt" + done + fi + fi + + # Test when \C is disabled + + if [ "$do23" = yes ] ; then + echo $title23 + if [ $supportBSC -ne 0 ] ; then + echo " Skipped because \C is not disabled" + else + $sim $valgrind ${opt:+$vjs} ./pcre2test -q $defaultstack $bmode $opt $testdata/testinput23 testtry + checkresult $? 23 "" + fi fi # End of loop for 8/16/32-bit tests diff --git a/pcre2-10.20/RunTest.bat b/pcre2-10.21/RunTest.bat similarity index 77% rename from pcre2-10.20/RunTest.bat rename to pcre2-10.21/RunTest.bat index 45bdfcbcb..ce9d5b577 100644 --- a/pcre2-10.20/RunTest.bat +++ b/pcre2-10.21/RunTest.bat @@ -13,17 +13,18 @@ @rem line. Added argument validation and added error reporting. @rem @rem Sheri Pierce added logic to skip feature dependent tests -@rem tests 4 5 9 15 and 18 require utf support -@rem tests 6 7 10 16 and 19 require ucp support -@rem 11 requires ucp and link size 2 -@rem 12 requires presence of jit support -@rem 13 requires absence of jit support +@rem tests 4 5 7 10 12 14 19 and 22 require Unicode support +@rem 8 requires Unicode and link size 2 +@rem 16 requires absence of jit support +@rem 17 requires presence of jit support @rem Sheri P also added override tests for study and jit testing @rem Zoltan Herczeg added libpcre16 support @rem Zoltan Herczeg added libpcre32 support @rem ------------------------------------------------------------------- @rem @rem The file was converted for PCRE2 by PH, February 2015. +@rem Updated for new test 14 (moving others up a number), August 2015. +@rem Tidied and updated for new tests 21, 22, 23 by PH, October 2015. setlocal enabledelayedexpansion @@ -64,6 +65,8 @@ set support32=%ERRORLEVEL% set unicode=%ERRORLEVEL% %pcre2test% -C jit >NUL set jit=%ERRORLEVEL% +%pcre2test% -C backslash-C >NUL +set supportBSC=%ERRORLEVEL% if %support8% EQU 1 ( if not exist testout8 md testout8 @@ -99,18 +102,22 @@ set do16=no set do17=no set do18=no set do19=no +set do20=no +set do21=no +set do22=no +set do23=no set all=yes for %%a in (%*) do ( set valid=no - for %%v in (1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19) do if %%v == %%a set valid=yes + for %%v in (1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23) do if %%v == %%a set valid=yes if "!valid!" == "yes" ( set do%%a=yes set all=no ) else ( echo Invalid test number - %%a! echo Usage %0 [ test_number ] ... - echo Where test_number is one or more optional test numbers 1 through 19, default is all tests. + echo Where test_number is one or more optional test numbers 1 through 23, default is all tests. exit /b 1 ) ) @@ -136,6 +143,10 @@ if "%all%" == "yes" ( set do17=yes set do18=yes set do19=yes + set do20=yes + set do21=yes + set do22=yes + set do23=yes ) @echo RunTest.bat's pcre2test output is written to newly created subfolders @@ -183,6 +194,10 @@ if "%do16%" == "yes" call :do16 if "%do17%" == "yes" call :do17 if "%do18%" == "yes" call :do18 if "%do19%" == "yes" call :do19 +if "%do20%" == "yes" call :do20 +if "%do21%" == "yes" call :do21 +if "%do22%" == "yes" call :do22 +if "%do23%" == "yes" call :do23 :modeSkip if "%mode%" == "" ( set mode=-16 @@ -253,6 +268,9 @@ if [%1]==[11] ( if [%1]==[12] ( set type=-%bits% ) +if [%1]==[14] ( + set type=-%bits% +) fc /n %srcdir%\testdata\%testoutput%%type% %2%bits%\%testoutput% >NUL @@ -316,7 +334,7 @@ if %unicode% EQU 0 ( goto :eof :do6 - call :runsub 6 testout "DFA matching main non-UTF, non-UCP functionality" -q -dfa + call :runsub 6 testout "DFA matching main non-UTF, non-UCP functionality" -q goto :eof :do7 @@ -324,7 +342,7 @@ if %unicode% EQU 0 ( echo Test 7 Skipped due to absence of Unicode support. goto :eof ) - call :runsub 7 testout "DFA matching with UTF-%bits% and Unicode property support" -q -dfa + call :runsub 7 testout "DFA matching with UTF-%bits% and Unicode property support" -q goto :eof :do8 @@ -388,39 +406,35 @@ if %bits% EQU 8 ( echo Test 13 Skipped when running 8-bit tests. goto :eof ) - call :runsub 13 testout "DFA specials for the basic 16/32-bit library" -q -dfa + call :runsub 13 testout "DFA specials for the basic 16/32-bit library" -q goto :eof :do14 -call :runsub 14 testout "Non-JIT limits and other non_JIT tests" -q -goto :eof - -:do15 -if %jit% EQU 1 ( - echo Test 15 Skipped due to presence of JIT support. +if %unicode% EQU 0 ( + echo Test 14 Skipped due to absence of Unicode support. goto :eof ) - call :runsub 15 testout "JIT-specific features when JIT is not available" -q + call :runsub 14 testout "DFA specials for UTF and UCP support" -q + goto :eof + +:do15 +call :runsub 15 testout "Non-JIT limits and other non_JIT tests" -q goto :eof :do16 -if %jit% EQU 0 ( - echo Test 16 Skipped due to absence of JIT support. +if %jit% EQU 1 ( + echo Test 16 Skipped due to presence of JIT support. goto :eof ) - call :runsub 16 testout "JIT-specific features when JIT is available" -q + call :runsub 16 testout "JIT-specific features when JIT is not available" -q goto :eof :do17 -if %bits% EQU 16 ( - echo Test 17 Skipped when running 16-bit tests. +if %jit% EQU 0 ( + echo Test 17 Skipped due to absence of JIT support. goto :eof ) -if %bits% EQU 32 ( - echo Test 17 Skipped when running 32-bit tests. - goto :eof -) - call :runsub 17 testout "POSIX interface, excluding UTF-8 and UCP" -q + call :runsub 17 testout "JIT-specific features when JIT is available" -q goto :eof :do18 @@ -432,11 +446,58 @@ if %bits% EQU 32 ( echo Test 18 Skipped when running 32-bit tests. goto :eof ) - call :runsub 1 testout "POSIX interface with UTF-8 and UCP" -q + call :runsub 18 testout "POSIX interface, excluding UTF-8 and UCP" -q goto :eof :do19 -call :runsub 1 testout "Serialization tests" -q +if %bits% EQU 16 ( + echo Test 19 Skipped when running 16-bit tests. + goto :eof +) +if %bits% EQU 32 ( + echo Test 19 Skipped when running 32-bit tests. + goto :eof +) +if %unicode% EQU 0 ( + echo Test 19 Skipped due to absence of Unicode support. + goto :eof +) + call :runsub 19 testout "POSIX interface with UTF-8 and UCP" -q +goto :eof + +:do20 +call :runsub 20 testout "Serialization tests" -q +goto :eof + +:do21 +if %supportBSC% EQU 0 ( + echo Test 21 Skipped due to absence of backslash-C support. + goto :eof +) + call :runsub 21 testout "Backslash-C tests without UTF" -q + call :runsub 21 testout "Backslash-C tests without UTF (DFA)" -q -dfa + if %jit% EQU 1 call :runsub 21 testoutjit "Test with JIT Override" -q -jit +goto :eof + +:do22 +if %supportBSC% EQU 0 ( + echo Test 22 Skipped due to absence of backslash-C support. + goto :eof +) +if %unicode% EQU 0 ( + echo Test 22 Skipped due to absence of Unicode support. + goto :eof +) + call :runsub 22 testout "Backslash-C tests with UTF" -q + if %jit% EQU 1 call :runsub 22 testoutjit "Test with JIT Override" -q -jit +goto :eof + +:do23 +if %supportBSC% EQU 1 ( + echo Test 23 Skipped due to presence of backslash-C support. + goto :eof +) + call :runsub 23 testout "Backslash-C disabled test" -q goto :eof :conferror diff --git a/pcre2-10.20/aclocal.m4 b/pcre2-10.21/aclocal.m4 similarity index 89% rename from pcre2-10.20/aclocal.m4 rename to pcre2-10.21/aclocal.m4 index db6146ab0..10c9cd1c9 100644 --- a/pcre2-10.20/aclocal.m4 +++ b/pcre2-10.21/aclocal.m4 @@ -20,32 +20,63 @@ You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically 'autoreconf'.])]) -# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- -# serial 1 (pkg-config-0.24) -# -# Copyright © 2004 Scott James Remnant . -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. +dnl pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +dnl serial 11 (pkg-config-0.29) +dnl +dnl Copyright © 2004 Scott James Remnant . +dnl Copyright © 2012-2015 Dan Nicholson +dnl +dnl This program is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 2 of the License, or +dnl (at your option) any later version. +dnl +dnl This program is distributed in the hope that it will be useful, but +dnl WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +dnl General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program; if not, write to the Free Software +dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +dnl 02111-1307, USA. +dnl +dnl As a special exception to the GNU General Public License, if you +dnl distribute this file as part of a program that contains a +dnl configuration script generated by Autoconf, you may include it under +dnl the same distribution terms that you use for the rest of that +dnl program. -# PKG_PROG_PKG_CONFIG([MIN-VERSION]) -# ---------------------------------- +dnl PKG_PREREQ(MIN-VERSION) +dnl ----------------------- +dnl Since: 0.29 +dnl +dnl Verify that the version of the pkg-config macros are at least +dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's +dnl installed version of pkg-config, this checks the developer's version +dnl of pkg.m4 when generating configure. +dnl +dnl To ensure that this macro is defined, also add: +dnl m4_ifndef([PKG_PREREQ], +dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])]) +dnl +dnl See the "Since" comment for each macro you use to see what version +dnl of the macros you require. +m4_defun([PKG_PREREQ], +[m4_define([PKG_MACROS_VERSION], [0.29]) +m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, + [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) +])dnl PKG_PREREQ + +dnl PKG_PROG_PKG_CONFIG([MIN-VERSION]) +dnl ---------------------------------- +dnl Since: 0.16 +dnl +dnl Search for the pkg-config tool and set the PKG_CONFIG variable to +dnl first found in the path. Checks that the version of pkg-config found +dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is +dnl used since that's the first version where most current features of +dnl pkg-config existed. AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) @@ -67,18 +98,19 @@ if test -n "$PKG_CONFIG"; then PKG_CONFIG="" fi fi[]dnl -])# PKG_PROG_PKG_CONFIG +])dnl PKG_PROG_PKG_CONFIG -# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) -# -# Check to see whether a particular set of modules exists. Similar -# to PKG_CHECK_MODULES(), but does not set variables or print errors. -# -# Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) -# only at the first occurence in configure.ac, so if the first place -# it's called might be skipped (such as if it is within an "if", you -# have to call PKG_CHECK_EXISTS manually -# -------------------------------------------------------------- +dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl ------------------------------------------------------------------- +dnl Since: 0.18 +dnl +dnl Check to see whether a particular set of modules exists. Similar to +dnl PKG_CHECK_MODULES(), but does not set variables or print errors. +dnl +dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +dnl only at the first occurence in configure.ac, so if the first place +dnl it's called might be skipped (such as if it is within an "if", you +dnl have to call PKG_CHECK_EXISTS manually AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ @@ -88,8 +120,10 @@ m4_ifvaln([$3], [else $3])dnl fi]) -# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) -# --------------------------------------------- +dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) +dnl --------------------------------------------- +dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting +dnl pkg_failed based on the result. m4_define([_PKG_CONFIG], [if test -n "$$1"; then pkg_cv_[]$1="$$1" @@ -101,10 +135,11 @@ m4_define([_PKG_CONFIG], else pkg_failed=untried fi[]dnl -])# _PKG_CONFIG +])dnl _PKG_CONFIG -# _PKG_SHORT_ERRORS_SUPPORTED -# ----------------------------- +dnl _PKG_SHORT_ERRORS_SUPPORTED +dnl --------------------------- +dnl Internal check to see if pkg-config supports short errors. AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then @@ -112,19 +147,17 @@ if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then else _pkg_short_errors_supported=no fi[]dnl -])# _PKG_SHORT_ERRORS_SUPPORTED +])dnl _PKG_SHORT_ERRORS_SUPPORTED -# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], -# [ACTION-IF-NOT-FOUND]) -# -# -# Note that if there is a possibility the first call to -# PKG_CHECK_MODULES might not happen, you should be sure to include an -# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac -# -# -# -------------------------------------------------------------- +dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +dnl [ACTION-IF-NOT-FOUND]) +dnl -------------------------------------------------------------- +dnl Since: 0.4.0 +dnl +dnl Note that if there is a possibility the first call to +dnl PKG_CHECK_MODULES might not happen, you should be sure to include an +dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl @@ -178,16 +211,40 @@ else AC_MSG_RESULT([yes]) $3 fi[]dnl -])# PKG_CHECK_MODULES +])dnl PKG_CHECK_MODULES -# PKG_INSTALLDIR(DIRECTORY) -# ------------------------- -# Substitutes the variable pkgconfigdir as the location where a module -# should install pkg-config .pc files. By default the directory is -# $libdir/pkgconfig, but the default can be changed by passing -# DIRECTORY. The user can override through the --with-pkgconfigdir -# parameter. +dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +dnl [ACTION-IF-NOT-FOUND]) +dnl --------------------------------------------------------------------- +dnl Since: 0.29 +dnl +dnl Checks for existence of MODULES and gathers its build flags with +dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags +dnl and VARIABLE-PREFIX_LIBS from --libs. +dnl +dnl Note that if there is a possibility the first call to +dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to +dnl include an explicit call to PKG_PROG_PKG_CONFIG in your +dnl configure.ac. +AC_DEFUN([PKG_CHECK_MODULES_STATIC], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +_save_PKG_CONFIG=$PKG_CONFIG +PKG_CONFIG="$PKG_CONFIG --static" +PKG_CHECK_MODULES($@) +PKG_CONFIG=$_save_PKG_CONFIG[]dnl +])dnl PKG_CHECK_MODULES_STATIC + + +dnl PKG_INSTALLDIR([DIRECTORY]) +dnl ------------------------- +dnl Since: 0.27 +dnl +dnl Substitutes the variable pkgconfigdir as the location where a module +dnl should install pkg-config .pc files. By default the directory is +dnl $libdir/pkgconfig, but the default can be changed by passing +dnl DIRECTORY. The user can override through the --with-pkgconfigdir +dnl parameter. AC_DEFUN([PKG_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) m4_pushdef([pkg_description], @@ -198,16 +255,18 @@ AC_ARG_WITH([pkgconfigdir], AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) -]) dnl PKG_INSTALLDIR +])dnl PKG_INSTALLDIR -# PKG_NOARCH_INSTALLDIR(DIRECTORY) -# ------------------------- -# Substitutes the variable noarch_pkgconfigdir as the location where a -# module should install arch-independent pkg-config .pc files. By -# default the directory is $datadir/pkgconfig, but the default can be -# changed by passing DIRECTORY. The user can override through the -# --with-noarch-pkgconfigdir parameter. +dnl PKG_NOARCH_INSTALLDIR([DIRECTORY]) +dnl -------------------------------- +dnl Since: 0.27 +dnl +dnl Substitutes the variable noarch_pkgconfigdir as the location where a +dnl module should install arch-independent pkg-config .pc files. By +dnl default the directory is $datadir/pkgconfig, but the default can be +dnl changed by passing DIRECTORY. The user can override through the +dnl --with-noarch-pkgconfigdir parameter. AC_DEFUN([PKG_NOARCH_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) m4_pushdef([pkg_description], @@ -218,13 +277,15 @@ AC_ARG_WITH([noarch-pkgconfigdir], AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) -]) dnl PKG_NOARCH_INSTALLDIR +])dnl PKG_NOARCH_INSTALLDIR -# PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, -# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) -# ------------------------------------------- -# Retrieves the value of the pkg-config variable for the given module. +dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, +dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl ------------------------------------------- +dnl Since: 0.28 +dnl +dnl Retrieves the value of the pkg-config variable for the given module. AC_DEFUN([PKG_CHECK_VAR], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl @@ -233,7 +294,7 @@ _PKG_CONFIG([$1], [variable="][$3]["], [$2]) AS_VAR_COPY([$1], [pkg_cv_][$1]) AS_VAR_IF([$1], [""], [$5], [$4])dnl -])# PKG_CHECK_VAR +])dnl PKG_CHECK_VAR # Copyright (C) 2002-2014 Free Software Foundation, Inc. # @@ -920,42 +981,6 @@ fi rmdir .tst 2>/dev/null AC_SUBST([am__leading_dot])]) -# Add --enable-maintainer-mode option to configure. -*- Autoconf -*- -# From Jim Meyering - -# Copyright (C) 1996-2014 Free Software Foundation, Inc. -# -# This file is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# AM_MAINTAINER_MODE([DEFAULT-MODE]) -# ---------------------------------- -# Control maintainer-specific portions of Makefiles. -# Default is to disable them, unless 'enable' is passed literally. -# For symmetry, 'disable' may be passed as well. Anyway, the user -# can override the default with the --enable/--disable switch. -AC_DEFUN([AM_MAINTAINER_MODE], -[m4_case(m4_default([$1], [disable]), - [enable], [m4_define([am_maintainer_other], [disable])], - [disable], [m4_define([am_maintainer_other], [enable])], - [m4_define([am_maintainer_other], [enable]) - m4_warn([syntax], [unexpected argument to AM@&t@_MAINTAINER_MODE: $1])]) -AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) - dnl maintainer-mode's default is 'disable' unless 'enable' is passed - AC_ARG_ENABLE([maintainer-mode], - [AS_HELP_STRING([--]am_maintainer_other[-maintainer-mode], - am_maintainer_other[ make rules and dependencies not useful - (and sometimes confusing) to the casual installer])], - [USE_MAINTAINER_MODE=$enableval], - [USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes])) - AC_MSG_RESULT([$USE_MAINTAINER_MODE]) - AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes]) - MAINT=$MAINTAINER_MODE_TRUE - AC_SUBST([MAINT])dnl -] -) - # Check to see how 'make' treats includes. -*- Autoconf -*- # Copyright (C) 2001-2014 Free Software Foundation, Inc. diff --git a/pcre2-10.20/ar-lib b/pcre2-10.21/ar-lib similarity index 100% rename from pcre2-10.20/ar-lib rename to pcre2-10.21/ar-lib diff --git a/pcre2-10.20/cmake/COPYING-CMAKE-SCRIPTS b/pcre2-10.21/cmake/COPYING-CMAKE-SCRIPTS similarity index 100% rename from pcre2-10.20/cmake/COPYING-CMAKE-SCRIPTS rename to pcre2-10.21/cmake/COPYING-CMAKE-SCRIPTS diff --git a/pcre2-10.20/cmake/FindEditline.cmake b/pcre2-10.21/cmake/FindEditline.cmake similarity index 100% rename from pcre2-10.20/cmake/FindEditline.cmake rename to pcre2-10.21/cmake/FindEditline.cmake diff --git a/pcre2-10.20/cmake/FindPackageHandleStandardArgs.cmake b/pcre2-10.21/cmake/FindPackageHandleStandardArgs.cmake similarity index 100% rename from pcre2-10.20/cmake/FindPackageHandleStandardArgs.cmake rename to pcre2-10.21/cmake/FindPackageHandleStandardArgs.cmake diff --git a/pcre2-10.20/cmake/FindReadline.cmake b/pcre2-10.21/cmake/FindReadline.cmake similarity index 100% rename from pcre2-10.20/cmake/FindReadline.cmake rename to pcre2-10.21/cmake/FindReadline.cmake diff --git a/pcre2-10.20/compile b/pcre2-10.21/compile similarity index 100% rename from pcre2-10.20/compile rename to pcre2-10.21/compile diff --git a/pcre2-10.20/config-cmake.h.in b/pcre2-10.21/config-cmake.h.in similarity index 97% rename from pcre2-10.20/config-cmake.h.in rename to pcre2-10.21/config-cmake.h.in index b74a7aaab..0cfd8b134 100644 --- a/pcre2-10.20/config-cmake.h.in +++ b/pcre2-10.21/config-cmake.h.in @@ -33,6 +33,7 @@ #cmakedefine EBCDIC 1 #cmakedefine EBCDIC_NL25 1 #cmakedefine HEAP_MATCH_RECURSE 1 +#cmakedefine NEVER_BACKSLASH_C 1 #define LINK_SIZE @PCRE2_LINK_SIZE@ #define MATCH_LIMIT @PCRE2_MATCH_LIMIT@ diff --git a/pcre2-10.20/config.guess b/pcre2-10.21/config.guess similarity index 100% rename from pcre2-10.20/config.guess rename to pcre2-10.21/config.guess diff --git a/pcre2-10.20/config.sub b/pcre2-10.21/config.sub similarity index 100% rename from pcre2-10.20/config.sub rename to pcre2-10.21/config.sub diff --git a/pcre2-10.20/configure b/pcre2-10.21/configure similarity index 99% rename from pcre2-10.20/configure rename to pcre2-10.21/configure index 21e553b3c..e9f9e374d 100755 --- a/pcre2-10.20/configure +++ b/pcre2-10.21/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for PCRE2 10.20. +# Generated by GNU Autoconf 2.69 for PCRE2 10.21. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -587,8 +587,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='PCRE2' PACKAGE_TARNAME='pcre2' -PACKAGE_VERSION='10.20' -PACKAGE_STRING='PCRE2 10.20' +PACKAGE_VERSION='10.21' +PACKAGE_STRING='PCRE2 10.21' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -734,9 +734,6 @@ CFLAGS CC ac_ct_AR AR -MAINT -MAINTAINER_MODE_FALSE -MAINTAINER_MODE_TRUE AM_BACKSLASH AM_DEFAULT_VERBOSITY AM_DEFAULT_V @@ -806,7 +803,6 @@ ac_subst_files='' ac_user_opts=' enable_option_checking enable_silent_rules -enable_maintainer_mode enable_dependency_tracking enable_shared enable_static @@ -833,6 +829,7 @@ enable_newline_is_crlf enable_newline_is_anycrlf enable_newline_is_any enable_bsr_anycrlf +enable_never_backslash_C enable_ebcdic enable_ebcdic_nl25 enable_stack_for_recursion @@ -1405,7 +1402,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures PCRE2 10.20 to adapt to many kinds of systems. +\`configure' configures PCRE2 10.21 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1475,7 +1472,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of PCRE2 10.20:";; + short | recursive ) echo "Configuration of PCRE2 10.21:";; esac cat <<\_ACEOF @@ -1485,9 +1482,6 @@ Optional Features: --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-silent-rules less verbose build output (undo: "make V=1") --disable-silent-rules verbose build output (undo: "make V=0") - --enable-maintainer-mode - enable make rules and dependencies not useful (and - sometimes confusing) to the casual installer --enable-dependency-tracking do not reject slow dependency extractors --disable-dependency-tracking @@ -1515,6 +1509,8 @@ Optional Features: use CR, LF, or CRLF as newline sequence --enable-newline-is-any use any valid Unicode newline sequence --enable-bsr-anycrlf \R matches only CR, LF, CRLF by default + --enable-never-backslash-C + use of \C causes an error --enable-ebcdic assume EBCDIC coding rather than ASCII; incompatible with --enable-utf; use only in (uncommon) EBCDIC environments; it implies --enable-rebuild-chartables @@ -1642,7 +1638,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -PCRE2 configure 10.20 +PCRE2 configure 10.21 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2137,7 +2133,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by PCRE2 $as_me 10.20, which was +It was created by PCRE2 $as_me 10.21, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -3001,7 +2997,7 @@ fi # Define the identity of the package. PACKAGE='pcre2' - VERSION='10.20' + VERSION='10.21' cat >>confdefs.h <<_ACEOF @@ -3136,33 +3132,6 @@ AM_BACKSLASH='\' ac_config_headers="$ac_config_headers src/config.h" -# FISH PATCH -# Enable maintainer mode to avoid spurious rebuilds due to timestamps in git -# not being stored. Discussion in https://github.com/fish-shell/fish-shell/issues/2469 - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5 -$as_echo_n "checking whether to enable maintainer-specific portions of Makefiles... " >&6; } - # Check whether --enable-maintainer-mode was given. -if test "${enable_maintainer_mode+set}" = set; then : - enableval=$enable_maintainer_mode; USE_MAINTAINER_MODE=$enableval -else - USE_MAINTAINER_MODE=no -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_MAINTAINER_MODE" >&5 -$as_echo "$USE_MAINTAINER_MODE" >&6; } - if test $USE_MAINTAINER_MODE = yes; then - MAINTAINER_MODE_TRUE= - MAINTAINER_MODE_FALSE='#' -else - MAINTAINER_MODE_TRUE='#' - MAINTAINER_MODE_FALSE= -fi - - MAINT=$MAINTAINER_MODE_TRUE - - - # This is a new thing required to stop a warning from automake 1.12 DEPDIR="${am__leading_dot}deps" @@ -13624,9 +13593,9 @@ _ACEOF # Versioning PCRE2_MAJOR="10" -PCRE2_MINOR="20" +PCRE2_MINOR="21" PCRE2_PRERELEASE="" -PCRE2_DATE="2015-06-30" +PCRE2_DATE="2016-01-12" if test "$PCRE2_MINOR" = "08" -o "$PCRE2_MINOR" = "09" then @@ -13790,6 +13759,15 @@ else fi +# Handle --enable-never-backslash-C +# Check whether --enable-never-backslash-C was given. +if test "${enable_never_backslash_C+set}" = set; then : + enableval=$enable_never_backslash_C; +else + enable_never_backslash_C=no +fi + + # Handle --enable-ebcdic # Check whether --enable-ebcdic was given. if test "${enable_ebcdic+set}" = set; then : @@ -13980,13 +13958,17 @@ fi # Make sure that if enable_ebcdic is set, rebuild_chartables is also enabled. # Also check that UTF support is not requested, because PCRE2 cannot handle # EBCDIC and UTF in the same build. To do so it would need to use different -# character constants depending on the mode. +# character constants depending on the mode. Also, EBCDIC cannot be used with +# 16-bit and 32-bit libraries. # if test "x$enable_ebcdic" = "xyes"; then enable_rebuild_chartables=yes if test "x$enable_unicode" = "xyes"; then as_fn_error $? "support for EBCDIC and Unicode cannot be enabled at the same time" "$LINENO" 5 fi + if test "x$enable_pcre2_16" = "xyes" -o "x$enable_pcre2_32" = "xyes"; then + as_fn_error $? "EBCDIC support is available only for the 8-bit library" "$LINENO" 5 + fi fi # Check argument to --with-link-size @@ -15321,6 +15303,12 @@ $as_echo "#define BSR_ANYCRLF /**/" >>confdefs.h fi +if test "$enable_never_backslash_C" = "yes"; then + +$as_echo "#define NEVER_BACKSLASH_C /**/" >>confdefs.h + +fi + cat >>confdefs.h <<_ACEOF #define LINK_SIZE $with_link_size @@ -15395,16 +15383,16 @@ esac # are m4 variables, assigned above. EXTRA_LIBPCRE2_8_LDFLAGS="$EXTRA_LIBPCRE2_8_LDFLAGS \ - $NO_UNDEFINED -version-info 2:0:2" + $NO_UNDEFINED -version-info 3:0:3" EXTRA_LIBPCRE2_16_LDFLAGS="$EXTRA_LIBPCRE2_16_LDFLAGS \ - $NO_UNDEFINED -version-info 2:0:2" + $NO_UNDEFINED -version-info 3:0:3" EXTRA_LIBPCRE2_32_LDFLAGS="$EXTRA_LIBPCRE2_32_LDFLAGS \ - $NO_UNDEFINED -version-info 2:0:2" + $NO_UNDEFINED -version-info 3:0:3" EXTRA_LIBPCRE2_POSIX_LDFLAGS="$EXTRA_LIBPCRE2_POSIX_LDFLAGS \ - $NO_UNDEFINED -version-info 0:0:0" + $NO_UNDEFINED -version-info 0:1:0" @@ -16002,10 +15990,6 @@ else am__EXEEXT_FALSE= fi -if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then - as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined. -Usually this means the macro was only invoked conditionally." "$LINENO" 5 -fi if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then as_fn_error $? "conditional \"AMDEP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 @@ -16451,7 +16435,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by PCRE2 $as_me 10.20, which was +This file was extended by PCRE2 $as_me 10.21, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -16517,7 +16501,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -PCRE2 config.status 10.20 +PCRE2 config.status 10.21 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" @@ -18249,6 +18233,7 @@ $PACKAGE-$VERSION configuration summary: Enable Unicode support .......... : ${enable_unicode} Newline char/sequence ........... : ${enable_newline} \R matches only ANYCRLF ......... : ${enable_bsr_anycrlf} + \C is disabled .................. : ${enable_never_backslash_C} EBCDIC coding ................... : ${enable_ebcdic} EBCDIC code for NL .............. : ${ebcdic_nl_code} Rebuild char tables ............. : ${enable_rebuild_chartables} diff --git a/pcre2-10.20/configure.ac b/pcre2-10.21/configure.ac similarity index 97% rename from pcre2-10.20/configure.ac rename to pcre2-10.21/configure.ac index ad6ed7fde..99ad8a9f0 100644 --- a/pcre2-10.20/configure.ac +++ b/pcre2-10.21/configure.ac @@ -9,18 +9,18 @@ dnl The PCRE2_PRERELEASE feature is for identifying release candidates. It might dnl be defined as -RC2, for example. For real releases, it should be empty. m4_define(pcre2_major, [10]) -m4_define(pcre2_minor, [20]) +m4_define(pcre2_minor, [21]) m4_define(pcre2_prerelease, []) -m4_define(pcre2_date, [2015-06-30]) +m4_define(pcre2_date, [2016-01-12]) # NOTE: The CMakeLists.txt file searches for the above variables in the first # 50 lines of this file. Please update that if the variables above are moved. # Libtool shared library interface versions (current:revision:age) -m4_define(libpcre2_8_version, [2:0:2]) -m4_define(libpcre2_16_version, [2:0:2]) -m4_define(libpcre2_32_version, [2:0:2]) -m4_define(libpcre2_posix_version, [0:0:0]) +m4_define(libpcre2_8_version, [3:0:3]) +m4_define(libpcre2_16_version, [3:0:3]) +m4_define(libpcre2_32_version, [3:0:3]) +m4_define(libpcre2_posix_version, [0:1:0]) AC_PREREQ(2.57) AC_INIT(PCRE2, pcre2_major.pcre2_minor[]pcre2_prerelease, , pcre2) @@ -29,11 +29,6 @@ AM_INIT_AUTOMAKE([dist-bzip2 dist-zip]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) AC_CONFIG_HEADERS(src/config.h) -# FISH PATCH -# Enable maintainer mode to avoid spurious rebuilds due to timestamps in git -# not being stored. Discussion in https://github.com/fish-shell/fish-shell/issues/2469 -AM_MAINTAINER_MODE - # This is a new thing required to stop a warning from automake 1.12 m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) @@ -195,6 +190,12 @@ AC_ARG_ENABLE(bsr-anycrlf, [\R matches only CR, LF, CRLF by default]), , enable_bsr_anycrlf=no) +# Handle --enable-never-backslash-C +AC_ARG_ENABLE(never-backslash-C, + AS_HELP_STRING([--enable-never-backslash-C], + [use of \C causes an error]), + , enable_never_backslash_C=no) + # Handle --enable-ebcdic AC_ARG_ENABLE(ebcdic, AS_HELP_STRING([--enable-ebcdic], @@ -338,13 +339,17 @@ fi # Make sure that if enable_ebcdic is set, rebuild_chartables is also enabled. # Also check that UTF support is not requested, because PCRE2 cannot handle # EBCDIC and UTF in the same build. To do so it would need to use different -# character constants depending on the mode. +# character constants depending on the mode. Also, EBCDIC cannot be used with +# 16-bit and 32-bit libraries. # if test "x$enable_ebcdic" = "xyes"; then enable_rebuild_chartables=yes if test "x$enable_unicode" = "xyes"; then AC_MSG_ERROR([support for EBCDIC and Unicode cannot be enabled at the same time]) fi + if test "x$enable_pcre2_16" = "xyes" -o "x$enable_pcre2_32" = "xyes"; then + AC_MSG_ERROR([EBCDIC support is available only for the 8-bit library]) + fi fi # Check argument to --with-link-size @@ -609,6 +614,11 @@ if test "$enable_bsr_anycrlf" = "yes"; then The build-time default can be overridden by the user of PCRE2 at runtime.]) fi +if test "$enable_never_backslash_C" = "yes"; then + AC_DEFINE([NEVER_BACKSLASH_C], [], [ + Defining NEVER_BACKSLASH_C locks out the use of \C in all patterns.]) +fi + AC_DEFINE_UNQUOTED([LINK_SIZE], [$with_link_size], [ The value of LINK_SIZE determines the number of bytes used to store links as offsets within the compiled regex. The default is 2, which @@ -886,6 +896,7 @@ $PACKAGE-$VERSION configuration summary: Enable Unicode support .......... : ${enable_unicode} Newline char/sequence ........... : ${enable_newline} \R matches only ANYCRLF ......... : ${enable_bsr_anycrlf} + \C is disabled .................. : ${enable_never_backslash_C} EBCDIC coding ................... : ${enable_ebcdic} EBCDIC code for NL .............. : ${ebcdic_nl_code} Rebuild char tables ............. : ${enable_rebuild_chartables} diff --git a/pcre2-10.20/depcomp b/pcre2-10.21/depcomp similarity index 100% rename from pcre2-10.20/depcomp rename to pcre2-10.21/depcomp diff --git a/pcre2-10.20/install-sh b/pcre2-10.21/install-sh similarity index 100% rename from pcre2-10.20/install-sh rename to pcre2-10.21/install-sh diff --git a/pcre2-10.20/libpcre2-16.pc.in b/pcre2-10.21/libpcre2-16.pc.in similarity index 100% rename from pcre2-10.20/libpcre2-16.pc.in rename to pcre2-10.21/libpcre2-16.pc.in diff --git a/pcre2-10.20/libpcre2-32.pc.in b/pcre2-10.21/libpcre2-32.pc.in similarity index 100% rename from pcre2-10.20/libpcre2-32.pc.in rename to pcre2-10.21/libpcre2-32.pc.in diff --git a/pcre2-10.20/libpcre2-8.pc.in b/pcre2-10.21/libpcre2-8.pc.in similarity index 100% rename from pcre2-10.20/libpcre2-8.pc.in rename to pcre2-10.21/libpcre2-8.pc.in diff --git a/pcre2-10.20/libpcre2-posix.pc.in b/pcre2-10.21/libpcre2-posix.pc.in similarity index 100% rename from pcre2-10.20/libpcre2-posix.pc.in rename to pcre2-10.21/libpcre2-posix.pc.in diff --git a/pcre2-10.20/ltmain.sh b/pcre2-10.21/ltmain.sh similarity index 100% rename from pcre2-10.20/ltmain.sh rename to pcre2-10.21/ltmain.sh diff --git a/pcre2-10.20/m4/ax_pthread.m4 b/pcre2-10.21/m4/ax_pthread.m4 similarity index 100% rename from pcre2-10.20/m4/ax_pthread.m4 rename to pcre2-10.21/m4/ax_pthread.m4 diff --git a/pcre2-10.20/m4/libtool.m4 b/pcre2-10.21/m4/libtool.m4 similarity index 100% rename from pcre2-10.20/m4/libtool.m4 rename to pcre2-10.21/m4/libtool.m4 diff --git a/pcre2-10.20/m4/ltoptions.m4 b/pcre2-10.21/m4/ltoptions.m4 similarity index 100% rename from pcre2-10.20/m4/ltoptions.m4 rename to pcre2-10.21/m4/ltoptions.m4 diff --git a/pcre2-10.20/m4/ltsugar.m4 b/pcre2-10.21/m4/ltsugar.m4 similarity index 100% rename from pcre2-10.20/m4/ltsugar.m4 rename to pcre2-10.21/m4/ltsugar.m4 diff --git a/pcre2-10.20/m4/ltversion.m4 b/pcre2-10.21/m4/ltversion.m4 similarity index 100% rename from pcre2-10.20/m4/ltversion.m4 rename to pcre2-10.21/m4/ltversion.m4 diff --git a/pcre2-10.20/m4/lt~obsolete.m4 b/pcre2-10.21/m4/lt~obsolete.m4 similarity index 100% rename from pcre2-10.20/m4/lt~obsolete.m4 rename to pcre2-10.21/m4/lt~obsolete.m4 diff --git a/pcre2-10.20/m4/pcre2_visibility.m4 b/pcre2-10.21/m4/pcre2_visibility.m4 similarity index 100% rename from pcre2-10.20/m4/pcre2_visibility.m4 rename to pcre2-10.21/m4/pcre2_visibility.m4 diff --git a/pcre2-10.20/missing b/pcre2-10.21/missing similarity index 100% rename from pcre2-10.20/missing rename to pcre2-10.21/missing diff --git a/pcre2-10.20/pcre2-config.in b/pcre2-10.21/pcre2-config.in similarity index 100% rename from pcre2-10.20/pcre2-config.in rename to pcre2-10.21/pcre2-config.in diff --git a/pcre2-10.20/perltest.sh b/pcre2-10.21/perltest.sh similarity index 98% rename from pcre2-10.20/perltest.sh rename to pcre2-10.21/perltest.sh index f011ccc99..9cf7b17f7 100755 --- a/pcre2-10.20/perltest.sh +++ b/pcre2-10.21/perltest.sh @@ -204,12 +204,14 @@ for (;;) printf "data> " if $interact; last NEXT_RE if ! ($_ = <$infile>); chomp; - printf $outfile "$_\n" if ! $interact; + printf $outfile "%s", "$_\n" if ! $interact; s/\s+$//; # Remove trailing space s/^\s+//; # Remove leading space last if ($_ eq ""); + next if $_ =~ /^\\=(?:\s|$)/; # Comment line + $x = eval "\"$_\""; # To get escapes processed # Empty array for holding results, ensure $REGERROR and $REGMARK are diff --git a/pcre2-10.20/src/config.h.generic b/pcre2-10.21/src/config.h.generic similarity index 98% rename from pcre2-10.20/src/config.h.generic rename to pcre2-10.21/src/config.h.generic index 0f9da50ce..744f19898 100644 --- a/pcre2-10.20/src/config.h.generic +++ b/pcre2-10.21/src/config.h.generic @@ -182,6 +182,9 @@ sure both macros are undefined; an emulation function will then be used. */ #define MAX_NAME_SIZE 32 #endif +/* Defining NEVER_BACKSLASH_C locks out the use of \C in all patterns. */ +/* #undef NEVER_BACKSLASH_C */ + /* The value of NEWLINE_DEFAULT determines the default newline character sequence. PCRE2 client programs can override this by selecting other values at run time. The valid values are 1 (CR), 2 (LF), 3 (CRLF), 4 (ANY), and 5 @@ -200,7 +203,7 @@ sure both macros are undefined; an emulation function will then be used. */ #define PACKAGE_NAME "PCRE2" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "PCRE2 10.20" +#define PACKAGE_STRING "PCRE2 10.21" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "pcre2" @@ -209,7 +212,7 @@ sure both macros are undefined; an emulation function will then be used. */ #define PACKAGE_URL "" /* Define to the version of this package. */ -#define PACKAGE_VERSION "10.20" +#define PACKAGE_VERSION "10.21" /* The value of PARENS_NEST_LIMIT specifies the maximum depth of nested parentheses (of any kind) in a pattern. This limits the amount of system @@ -290,7 +293,7 @@ sure both macros are undefined; an emulation function will then be used. */ /* #undef SUPPORT_VALGRIND */ /* Version number of package */ -#define VERSION "10.20" +#define VERSION "10.21" /* Define to empty if `const' does not conform to ANSI C. */ /* #undef const */ diff --git a/pcre2-10.20/src/config.h.in b/pcre2-10.21/src/config.h.in similarity index 99% rename from pcre2-10.20/src/config.h.in rename to pcre2-10.21/src/config.h.in index e3ef2fddc..e55d0a048 100644 --- a/pcre2-10.20/src/config.h.in +++ b/pcre2-10.21/src/config.h.in @@ -169,6 +169,9 @@ sure both macros are undefined; an emulation function will then be used. */ overflow caused by enormously large patterns. */ #undef MAX_NAME_SIZE +/* Defining NEVER_BACKSLASH_C locks out the use of \C in all patterns. */ +#undef NEVER_BACKSLASH_C + /* The value of NEWLINE_DEFAULT determines the default newline character sequence. PCRE2 client programs can override this by selecting other values at run time. The valid values are 1 (CR), 2 (LF), 3 (CRLF), 4 (ANY), and 5 diff --git a/pcre2-10.20/src/dftables.c b/pcre2-10.21/src/dftables.c similarity index 97% rename from pcre2-10.20/src/dftables.c rename to pcre2-10.21/src/dftables.c index b6417cc2e..dfb90b594 100644 --- a/pcre2-10.20/src/dftables.c +++ b/pcre2-10.21/src/dftables.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2014 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -102,7 +102,7 @@ fprintf(f, "/* This file was automatically written by the dftables auxiliary\n" "program. It contains character tables that are used when no external\n" "tables are passed to PCRE2 by the application that calls it. The tables\n" - "are used only for characters whose code values are less than 256.\n\n"); + "are used only for characters whose code values are less than 256. */\n\n"); /* Force config.h in z/OS */ @@ -115,7 +115,7 @@ fprintf(f, #endif fprintf(f, - "The following #includes are present because without them gcc 4.x may remove\n" + "/* The following #includes are present because without them gcc 4.x may remove\n" "the array definition from the final binary if PCRE2 is built into a static\n" "library and dead code stripping is activated. This leads to link errors.\n" "Pulling in the header ensures that the array gets flagged as \"someone\n" diff --git a/pcre2-10.20/src/pcre2.h.generic b/pcre2-10.21/src/pcre2.h.generic similarity index 95% rename from pcre2-10.20/src/pcre2.h.generic rename to pcre2-10.21/src/pcre2.h.generic index 3e97fb8bf..7f9ba4f19 100644 --- a/pcre2-10.20/src/pcre2.h.generic +++ b/pcre2-10.21/src/pcre2.h.generic @@ -5,7 +5,7 @@ /* This is the public header file for the PCRE library, second API, to be #included by applications that call PCRE2 functions. - Copyright (c) 2015 University of Cambridge + Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -42,9 +42,9 @@ POSSIBILITY OF SUCH DAMAGE. /* The current PCRE version information. */ #define PCRE2_MAJOR 10 -#define PCRE2_MINOR 20 +#define PCRE2_MINOR 21 #define PCRE2_PRERELEASE -#define PCRE2_DATE 2015-06-30 +#define PCRE2_DATE 2016-01-12 /* When an application links to a PCRE DLL in Windows, the symbols that are imported have to be identified as such. When building PCRE2, the appropriate @@ -120,6 +120,8 @@ D is inspected during pcre2_dfa_match() execution #define PCRE2_UTF 0x00080000u /* C J M D */ #define PCRE2_NEVER_BACKSLASH_C 0x00100000u /* C */ #define PCRE2_ALT_CIRCUMFLEX 0x00200000u /* J M D */ +#define PCRE2_ALT_VERBNAMES 0x00400000u /* C */ +#define PCRE2_USE_OFFSET_LIMIT 0x00800000u /* J M D */ /* These are for pcre2_jit_compile(). */ @@ -144,9 +146,13 @@ sanity checks). */ #define PCRE2_DFA_RESTART 0x00000040u #define PCRE2_DFA_SHORTEST 0x00000080u -/* This is an additional option for pcre2_substitute(). */ +/* These are additional options for pcre2_substitute(). */ -#define PCRE2_SUBSTITUTE_GLOBAL 0x00000100u +#define PCRE2_SUBSTITUTE_GLOBAL 0x00000100u +#define PCRE2_SUBSTITUTE_EXTENDED 0x00000200u +#define PCRE2_SUBSTITUTE_UNSET_EMPTY 0x00000400u +#define PCRE2_SUBSTITUTE_UNKNOWN_UNSET 0x00000800u +#define PCRE2_SUBSTITUTE_OVERFLOW_LENGTH 0x00001000u /* Newline and \R settings, for use in compile contexts. The newline values must be kept in step with values set in config.h and both sets must all be @@ -233,6 +239,12 @@ numbers must not be changed. */ #define PCRE2_ERROR_RECURSIONLIMIT (-53) #define PCRE2_ERROR_UNAVAILABLE (-54) #define PCRE2_ERROR_UNSET (-55) +#define PCRE2_ERROR_BADOFFSETLIMIT (-56) +#define PCRE2_ERROR_BADREPESCAPE (-57) +#define PCRE2_ERROR_REPMISSINGBRACE (-58) +#define PCRE2_ERROR_BADSUBSTITUTION (-59) +#define PCRE2_ERROR_BADSUBSPATTERN (-60) +#define PCRE2_ERROR_TOOMANYREPLACE (-61) /* Request types for pcre2_pattern_info() */ @@ -259,6 +271,7 @@ numbers must not be changed. */ #define PCRE2_INFO_NEWLINE 20 #define PCRE2_INFO_RECURSIONLIMIT 21 #define PCRE2_INFO_SIZE 22 +#define PCRE2_INFO_HASBACKSLASHC 23 /* Request types for pcre2_config(). */ @@ -291,6 +304,7 @@ define special values to indicate zero-terminated strings and unset offsets in the offset vector (ovector). */ #define PCRE2_SIZE size_t +#define PCRE2_SIZE_MAX SIZE_MAX #define PCRE2_ZERO_TERMINATED (~(PCRE2_SIZE)0) #define PCRE2_UNSET (~(PCRE2_SIZE)0) @@ -388,6 +402,8 @@ PCRE2_EXP_DECL void pcre2_compile_context_free(pcre2_compile_context *); \ PCRE2_EXP_DECL int pcre2_set_bsr(pcre2_compile_context *, uint32_t); \ PCRE2_EXP_DECL int pcre2_set_character_tables(pcre2_compile_context *, \ const unsigned char *); \ +PCRE2_EXP_DECL int pcre2_set_max_pattern_length(pcre2_compile_context *, \ + PCRE2_SIZE); \ PCRE2_EXP_DECL int pcre2_set_newline(pcre2_compile_context *, uint32_t); \ PCRE2_EXP_DECL int pcre2_set_parens_nest_limit(pcre2_compile_context *, \ uint32_t); \ @@ -405,6 +421,8 @@ PCRE2_EXP_DECL int pcre2_set_callout(pcre2_match_context *, \ int (*)(pcre2_callout_block *, void *), void *); \ PCRE2_EXP_DECL int pcre2_set_match_limit(pcre2_match_context *, \ uint32_t); \ +PCRE2_EXP_DECL int pcre2_set_offset_limit(pcre2_match_context *, \ + PCRE2_SIZE); \ PCRE2_EXP_DECL int pcre2_set_recursion_limit(pcre2_match_context *, \ uint32_t); \ PCRE2_EXP_DECL int pcre2_set_recursion_memory_management( \ @@ -606,8 +624,10 @@ pcre2_compile are called by application code. */ #define pcre2_set_character_tables PCRE2_SUFFIX(pcre2_set_character_tables_) #define pcre2_set_compile_recursion_guard PCRE2_SUFFIX(pcre2_set_compile_recursion_guard_) #define pcre2_set_match_limit PCRE2_SUFFIX(pcre2_set_match_limit_) +#define pcre2_set_max_pattern_length PCRE2_SUFFIX(pcre2_set_max_pattern_length_) #define pcre2_set_newline PCRE2_SUFFIX(pcre2_set_newline_) #define pcre2_set_parens_nest_limit PCRE2_SUFFIX(pcre2_set_parens_nest_limit_) +#define pcre2_set_offset_limit PCRE2_SUFFIX(pcre2_set_offset_limit_) #define pcre2_set_recursion_limit PCRE2_SUFFIX(pcre2_set_recursion_limit_) #define pcre2_set_recursion_memory_management PCRE2_SUFFIX(pcre2_set_recursion_memory_management_) #define pcre2_substitute PCRE2_SUFFIX(pcre2_substitute_) diff --git a/pcre2-10.20/src/pcre2.h.in b/pcre2-10.21/src/pcre2.h.in similarity index 96% rename from pcre2-10.20/src/pcre2.h.in rename to pcre2-10.21/src/pcre2.h.in index 94fbdd5b3..49f190965 100644 --- a/pcre2-10.20/src/pcre2.h.in +++ b/pcre2-10.21/src/pcre2.h.in @@ -5,7 +5,7 @@ /* This is the public header file for the PCRE library, second API, to be #included by applications that call PCRE2 functions. - Copyright (c) 2015 University of Cambridge + Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -120,6 +120,8 @@ D is inspected during pcre2_dfa_match() execution #define PCRE2_UTF 0x00080000u /* C J M D */ #define PCRE2_NEVER_BACKSLASH_C 0x00100000u /* C */ #define PCRE2_ALT_CIRCUMFLEX 0x00200000u /* J M D */ +#define PCRE2_ALT_VERBNAMES 0x00400000u /* C */ +#define PCRE2_USE_OFFSET_LIMIT 0x00800000u /* J M D */ /* These are for pcre2_jit_compile(). */ @@ -144,9 +146,13 @@ sanity checks). */ #define PCRE2_DFA_RESTART 0x00000040u #define PCRE2_DFA_SHORTEST 0x00000080u -/* This is an additional option for pcre2_substitute(). */ +/* These are additional options for pcre2_substitute(). */ -#define PCRE2_SUBSTITUTE_GLOBAL 0x00000100u +#define PCRE2_SUBSTITUTE_GLOBAL 0x00000100u +#define PCRE2_SUBSTITUTE_EXTENDED 0x00000200u +#define PCRE2_SUBSTITUTE_UNSET_EMPTY 0x00000400u +#define PCRE2_SUBSTITUTE_UNKNOWN_UNSET 0x00000800u +#define PCRE2_SUBSTITUTE_OVERFLOW_LENGTH 0x00001000u /* Newline and \R settings, for use in compile contexts. The newline values must be kept in step with values set in config.h and both sets must all be @@ -233,6 +239,12 @@ numbers must not be changed. */ #define PCRE2_ERROR_RECURSIONLIMIT (-53) #define PCRE2_ERROR_UNAVAILABLE (-54) #define PCRE2_ERROR_UNSET (-55) +#define PCRE2_ERROR_BADOFFSETLIMIT (-56) +#define PCRE2_ERROR_BADREPESCAPE (-57) +#define PCRE2_ERROR_REPMISSINGBRACE (-58) +#define PCRE2_ERROR_BADSUBSTITUTION (-59) +#define PCRE2_ERROR_BADSUBSPATTERN (-60) +#define PCRE2_ERROR_TOOMANYREPLACE (-61) /* Request types for pcre2_pattern_info() */ @@ -259,6 +271,7 @@ numbers must not be changed. */ #define PCRE2_INFO_NEWLINE 20 #define PCRE2_INFO_RECURSIONLIMIT 21 #define PCRE2_INFO_SIZE 22 +#define PCRE2_INFO_HASBACKSLASHC 23 /* Request types for pcre2_config(). */ @@ -291,6 +304,7 @@ define special values to indicate zero-terminated strings and unset offsets in the offset vector (ovector). */ #define PCRE2_SIZE size_t +#define PCRE2_SIZE_MAX SIZE_MAX #define PCRE2_ZERO_TERMINATED (~(PCRE2_SIZE)0) #define PCRE2_UNSET (~(PCRE2_SIZE)0) @@ -388,6 +402,8 @@ PCRE2_EXP_DECL void pcre2_compile_context_free(pcre2_compile_context *); \ PCRE2_EXP_DECL int pcre2_set_bsr(pcre2_compile_context *, uint32_t); \ PCRE2_EXP_DECL int pcre2_set_character_tables(pcre2_compile_context *, \ const unsigned char *); \ +PCRE2_EXP_DECL int pcre2_set_max_pattern_length(pcre2_compile_context *, \ + PCRE2_SIZE); \ PCRE2_EXP_DECL int pcre2_set_newline(pcre2_compile_context *, uint32_t); \ PCRE2_EXP_DECL int pcre2_set_parens_nest_limit(pcre2_compile_context *, \ uint32_t); \ @@ -405,6 +421,8 @@ PCRE2_EXP_DECL int pcre2_set_callout(pcre2_match_context *, \ int (*)(pcre2_callout_block *, void *), void *); \ PCRE2_EXP_DECL int pcre2_set_match_limit(pcre2_match_context *, \ uint32_t); \ +PCRE2_EXP_DECL int pcre2_set_offset_limit(pcre2_match_context *, \ + PCRE2_SIZE); \ PCRE2_EXP_DECL int pcre2_set_recursion_limit(pcre2_match_context *, \ uint32_t); \ PCRE2_EXP_DECL int pcre2_set_recursion_memory_management( \ @@ -606,8 +624,10 @@ pcre2_compile are called by application code. */ #define pcre2_set_character_tables PCRE2_SUFFIX(pcre2_set_character_tables_) #define pcre2_set_compile_recursion_guard PCRE2_SUFFIX(pcre2_set_compile_recursion_guard_) #define pcre2_set_match_limit PCRE2_SUFFIX(pcre2_set_match_limit_) +#define pcre2_set_max_pattern_length PCRE2_SUFFIX(pcre2_set_max_pattern_length_) #define pcre2_set_newline PCRE2_SUFFIX(pcre2_set_newline_) #define pcre2_set_parens_nest_limit PCRE2_SUFFIX(pcre2_set_parens_nest_limit_) +#define pcre2_set_offset_limit PCRE2_SUFFIX(pcre2_set_offset_limit_) #define pcre2_set_recursion_limit PCRE2_SUFFIX(pcre2_set_recursion_limit_) #define pcre2_set_recursion_memory_management PCRE2_SUFFIX(pcre2_set_recursion_memory_management_) #define pcre2_substitute PCRE2_SUFFIX(pcre2_substitute_) diff --git a/pcre2-10.20/src/pcre2_auto_possess.c b/pcre2-10.21/src/pcre2_auto_possess.c similarity index 93% rename from pcre2-10.20/src/pcre2_auto_possess.c rename to pcre2-10.21/src/pcre2_auto_possess.c index e99a2c44f..d4d2334d8 100644 --- a/pcre2-10.20/src/pcre2_auto_possess.c +++ b/pcre2-10.21/src/pcre2_auto_possess.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2014 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -171,64 +171,6 @@ static const uint8_t posspropstab[3][4] = { { ucp_L, ucp_N, ucp_P, ucp_Po } /* WORD */ }; -/* This table is used when converting repeating opcodes into possessified -versions as a result of an explicit possessive quantifier such as ++. A zero -value means there is no possessified version - in those cases the item in -question must be wrapped in ONCE brackets. The table is truncated at OP_CALLOUT -because all relevant opcodes are less than that. */ - -static const uint8_t opcode_possessify[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 15 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16 - 31 */ - - 0, /* NOTI */ - OP_POSSTAR, 0, /* STAR, MINSTAR */ - OP_POSPLUS, 0, /* PLUS, MINPLUS */ - OP_POSQUERY, 0, /* QUERY, MINQUERY */ - OP_POSUPTO, 0, /* UPTO, MINUPTO */ - 0, /* EXACT */ - 0, 0, 0, 0, /* POS{STAR,PLUS,QUERY,UPTO} */ - - OP_POSSTARI, 0, /* STARI, MINSTARI */ - OP_POSPLUSI, 0, /* PLUSI, MINPLUSI */ - OP_POSQUERYI, 0, /* QUERYI, MINQUERYI */ - OP_POSUPTOI, 0, /* UPTOI, MINUPTOI */ - 0, /* EXACTI */ - 0, 0, 0, 0, /* POS{STARI,PLUSI,QUERYI,UPTOI} */ - - OP_NOTPOSSTAR, 0, /* NOTSTAR, NOTMINSTAR */ - OP_NOTPOSPLUS, 0, /* NOTPLUS, NOTMINPLUS */ - OP_NOTPOSQUERY, 0, /* NOTQUERY, NOTMINQUERY */ - OP_NOTPOSUPTO, 0, /* NOTUPTO, NOTMINUPTO */ - 0, /* NOTEXACT */ - 0, 0, 0, 0, /* NOTPOS{STAR,PLUS,QUERY,UPTO} */ - - OP_NOTPOSSTARI, 0, /* NOTSTARI, NOTMINSTARI */ - OP_NOTPOSPLUSI, 0, /* NOTPLUSI, NOTMINPLUSI */ - OP_NOTPOSQUERYI, 0, /* NOTQUERYI, NOTMINQUERYI */ - OP_NOTPOSUPTOI, 0, /* NOTUPTOI, NOTMINUPTOI */ - 0, /* NOTEXACTI */ - 0, 0, 0, 0, /* NOTPOS{STARI,PLUSI,QUERYI,UPTOI} */ - - OP_TYPEPOSSTAR, 0, /* TYPESTAR, TYPEMINSTAR */ - OP_TYPEPOSPLUS, 0, /* TYPEPLUS, TYPEMINPLUS */ - OP_TYPEPOSQUERY, 0, /* TYPEQUERY, TYPEMINQUERY */ - OP_TYPEPOSUPTO, 0, /* TYPEUPTO, TYPEMINUPTO */ - 0, /* TYPEEXACT */ - 0, 0, 0, 0, /* TYPEPOS{STAR,PLUS,QUERY,UPTO} */ - - OP_CRPOSSTAR, 0, /* CRSTAR, CRMINSTAR */ - OP_CRPOSPLUS, 0, /* CRPLUS, CRMINPLUS */ - OP_CRPOSQUERY, 0, /* CRQUERY, CRMINQUERY */ - OP_CRPOSRANGE, 0, /* CRRANGE, CRMINRANGE */ - 0, 0, 0, 0, /* CRPOS{STAR,PLUS,QUERY,RANGE} */ - - 0, 0, 0, /* CLASS, NCLASS, XCLASS */ - 0, 0, /* REF, REFI */ - 0, 0, /* DNREF, DNREFI */ - 0, 0 /* RECURSE, CALLOUT */ -}; - #ifdef SUPPORT_UNICODE diff --git a/pcre2-10.20/src/pcre2_chartables.c.dist b/pcre2-10.21/src/pcre2_chartables.c.dist similarity index 100% rename from pcre2-10.20/src/pcre2_chartables.c.dist rename to pcre2-10.21/src/pcre2_chartables.c.dist diff --git a/pcre2-10.20/src/pcre2_compile.c b/pcre2-10.21/src/pcre2_compile.c similarity index 85% rename from pcre2-10.20/src/pcre2_compile.c rename to pcre2-10.21/src/pcre2_compile.c index 4a9e42e2c..d8528378e 100644 --- a/pcre2-10.20/src/pcre2_compile.c +++ b/pcre2-10.21/src/pcre2_compile.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2015 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -96,26 +96,27 @@ static BOOL * Code parameters and static tables * *************************************************/ -/* This value specifies the size of stack workspace, which is used during the -pre-compile phase when determining how much memory is required. The regex is -partly compiled into this space, but the compiled parts are discarded as soon -as they can be, so that hopefully there will never be an overrun. The code -does, however, check for an overrun. The largest amount I've seen used is 218, -so this number is very generous. +/* This value specifies the size of stack workspace, which is used in different +ways in the different pattern scans. The group-identifying pre-scan uses it to +handle nesting, and needs it to be 16-bit aligned. -The same workspace is used during the second, actual compile phase for -remembering forward references to groups so that they can be filled in at the -end. Each entry in this list occupies LINK_SIZE bytes, so even when LINK_SIZE -is 4 there is plenty of room for most patterns. However, the memory can get -filled up by repetitions of forward references, for example patterns like -/(?1){0,1999}(b)/, and one user did hit the limit. The code has been changed so -that the workspace is expanded in this situation. The value below is therefore -a minimum, and we put a maximum on it for safety. The minimum is now also -defined in terms of LINK_SIZE so that the size increase kicks in at the same -number of forward references in all cases. */ +During the first compiling phase, when determining how much memory is required, +the regex is partly compiled into this space, but the compiled parts are +discarded as soon as they can be, so that hopefully there will never be an +overrun. The code does, however, check for an overrun, which can occur for +pathological patterns. The size of the workspace depends on LINK_SIZE because +the length of compiled items varies with this. -#define COMPILE_WORK_SIZE (2048*LINK_SIZE) -#define COMPILE_WORK_SIZE_MAX (100*COMPILE_WORK_SIZE) +In the real compile phase, the workspace is used for remembering data about +numbered groups, provided there are not too many of them (if there are, extra +memory is acquired). For this phase the memory must be 32-bit aligned. Having +defined the size in code units, we set up C32_WORK_SIZE as the number of +elements in the 32-bit vector. */ + +#define COMPILE_WORK_SIZE (2048*LINK_SIZE) /* Size in code units */ + +#define C32_WORK_SIZE \ + ((COMPILE_WORK_SIZE * sizeof(PCRE2_UCHAR))/sizeof(uint32_t)) /* The overrun tests check for a slightly smaller size so that they detect the overrun before it actually does run off the end of the data block. */ @@ -160,6 +161,14 @@ have to check them every time. */ #define REQ_UNSET (-2) /* Not yet found anything */ #define REQ_NONE (-1) /* Found not fixed char */ +/* These flags are used in the groupinfo vector. */ + +#define GI_SET_COULD_BE_EMPTY 0x80000000u +#define GI_COULD_BE_EMPTY 0x40000000u +#define GI_NOT_FIXED_LENGTH 0x20000000u +#define GI_SET_FIXED_LENGTH 0x10000000u +#define GI_FIXED_LENGTH_MASK 0x0000ffffu + /* This bit (which is greater than any UTF value) is used to indicate that a variable contains a number of code units instead of an actual code point. */ @@ -270,7 +279,7 @@ in UTF-8 mode. It runs from '0' to 'z'. */ #ifndef EBCDIC #define ESCAPES_FIRST CHAR_0 #define ESCAPES_LAST CHAR_z -#define ESCAPES_UPPER_CASE (-32) /* Add this to upper case a letter */ +#define UPPER_CASE(c) (c-32) static const short int escapes[] = { 0, 0, @@ -323,11 +332,11 @@ because it is defined as 'a', which of course picks up the ASCII value. */ #if 'a' == 0x81 /* Check for a real EBCDIC environment */ #define ESCAPES_FIRST CHAR_a #define ESCAPES_LAST CHAR_9 -#define ESCAPES_UPPER_CASE (+64) /* Add this to upper case a letter */ +#define UPPER_CASE(c) (c+64) #else /* Testing in an ASCII environment */ #define ESCAPES_FIRST ((unsigned char)'\x81') /* EBCDIC 'a' */ #define ESCAPES_LAST ((unsigned char)'\xf9') /* EBCDIC '9' */ -#define ESCAPES_UPPER_CASE (-32) /* Add this to upper case a letter */ +#define UPPER_CASE(c) (c-32) #endif static const short int escapes[] = { @@ -573,17 +582,18 @@ static PCRE2_SPTR posix_substitutes[] = { #define PUBLIC_COMPILE_OPTIONS \ (PCRE2_ANCHORED|PCRE2_ALLOW_EMPTY_CLASS|PCRE2_ALT_BSUX|PCRE2_ALT_CIRCUMFLEX| \ - PCRE2_AUTO_CALLOUT|PCRE2_CASELESS|PCRE2_DOLLAR_ENDONLY|PCRE2_DOTALL| \ - PCRE2_DUPNAMES|PCRE2_EXTENDED|PCRE2_FIRSTLINE|PCRE2_MATCH_UNSET_BACKREF| \ - PCRE2_MULTILINE|PCRE2_NEVER_BACKSLASH_C|PCRE2_NEVER_UCP| \ - PCRE2_NEVER_UTF|PCRE2_NO_AUTO_CAPTURE|PCRE2_NO_AUTO_POSSESS| \ - PCRE2_NO_DOTSTAR_ANCHOR|PCRE2_NO_START_OPTIMIZE|PCRE2_NO_UTF_CHECK| \ - PCRE2_UCP|PCRE2_UNGREEDY|PCRE2_UTF) + PCRE2_ALT_VERBNAMES|PCRE2_AUTO_CALLOUT|PCRE2_CASELESS|PCRE2_DOLLAR_ENDONLY| \ + PCRE2_DOTALL|PCRE2_DUPNAMES|PCRE2_EXTENDED|PCRE2_FIRSTLINE| \ + PCRE2_MATCH_UNSET_BACKREF|PCRE2_MULTILINE|PCRE2_NEVER_BACKSLASH_C| \ + PCRE2_NEVER_UCP|PCRE2_NEVER_UTF|PCRE2_NO_AUTO_CAPTURE| \ + PCRE2_NO_AUTO_POSSESS|PCRE2_NO_DOTSTAR_ANCHOR|PCRE2_NO_START_OPTIMIZE| \ + PCRE2_NO_UTF_CHECK|PCRE2_UCP|PCRE2_UNGREEDY|PCRE2_USE_OFFSET_LIMIT| \ + PCRE2_UTF) /* Compile time error code numbers. They are given names so that they can more easily be tracked. When a new number is added, the tables called eint1 and -eint2 in pcre2posix.c must be updated, and a new error text must be added to -compile_error_texts in pcre2_error.c. */ +eint2 in pcre2posix.c may need to be updated, and a new error text must be +added to compile_error_texts in pcre2_error.c. */ enum { ERR0 = COMPILE_ERROR_BASE, ERR1, ERR2, ERR3, ERR4, ERR5, ERR6, ERR7, ERR8, ERR9, ERR10, @@ -594,7 +604,21 @@ enum { ERR0 = COMPILE_ERROR_BASE, ERR51, ERR52, ERR53, ERR54, ERR55, ERR56, ERR57, ERR58, ERR59, ERR60, ERR61, ERR62, ERR63, ERR64, ERR65, ERR66, ERR67, ERR68, ERR69, ERR70, ERR71, ERR72, ERR73, ERR74, ERR75, ERR76, ERR77, ERR78, ERR79, ERR80, - ERR81, ERR82, ERR83, ERR84 }; + ERR81, ERR82, ERR83, ERR84, ERR85, ERR86, ERR87, ERR88 }; + +/* Error codes that correspond to negative error codes returned by +find_fixedlength(). */ + +static int fixed_length_errors[] = + { + ERR0, /* Not an error */ + ERR0, /* Not an error; -1 is used for "process later" */ + ERR25, /* Lookbehind is not fixed length */ + ERR36, /* \C in lookbehind is not allowed */ + ERR87, /* Lookbehind is too long */ + ERR86, /* Pattern too complicated */ + ERR70 /* Internal error: unknown opcode encountered */ + }; /* This is a table of start-of-pattern options such as (*UTF) and settings such as (*LIMIT_MATCH=nnnn) and (*CRLF). For completeness and backward @@ -791,39 +815,76 @@ PUT(previous_callout, 1 + LINK_SIZE, length); *************************************************/ /* Scan a branch and compute the fixed length of subject that will match it, if -the length is fixed. This is needed for dealing with backward assertions. In +the length is fixed. This is needed for dealing with lookbehind assertions. In UTF mode, the result is in code units rather than bytes. The branch is temporarily terminated with OP_END when this function is called. -This function is called when a backward assertion is encountered, so that if it -fails, the error message can point to the correct place in the pattern. +This function is called when a lookbehind assertion is encountered, so that if +it fails, the error message can point to the correct place in the pattern. However, we cannot do this when the assertion contains subroutine calls, because they can be forward references. We solve this by remembering this case and doing the check at the end; a flag specifies which mode we are running in. -Arguments: - code points to the start of the pattern (the bracket) - utf TRUE in UTF mode - atend TRUE if called when the pattern is complete - cb the "compile data" structure - recurses chain of recurse_check to catch mutual recursion +Lookbehind lengths are held in 16-bit fields and the maximum value is defined +as LOOKBEHIND_MAX. -Returns: the fixed length, - or -1 if there is no fixed length, - or -2 if \C was encountered (in UTF-8 mode only) - or -3 if an OP_RECURSE item was encountered and atend is FALSE - or -4 if an unknown opcode was encountered (internal error) +Arguments: + code points to the start of the pattern (the bracket) + utf TRUE in UTF mode + atend TRUE if called when the pattern is complete + cb the "compile data" structure + recurses chain of recurse_check to catch mutual recursion + countptr pointer to counter, to catch over-complexity + +Returns: if non-negative, the fixed length, + or -1 if an OP_RECURSE item was encountered and atend is FALSE + or -2 if there is no fixed length, + or -3 if \C was encountered (in UTF-8 mode only) + or -4 length is too long + or -5 if an unknown opcode was encountered (internal error) */ +#define FFL_LATER (-1) +#define FFL_NOTFIXED (-2) +#define FFL_BACKSLASHC (-3) +#define FFL_TOOLONG (-4) +#define FFL_TOOCOMPLICATED (-5) +#define FFL_UNKNOWNOP (-6) + static int find_fixedlength(PCRE2_UCHAR *code, BOOL utf, BOOL atend, compile_block *cb, - recurse_check *recurses) + recurse_check *recurses, int *countptr) { int length = -1; +uint32_t group = 0; +uint32_t groupinfo = 0; recurse_check this_recurse; register int branchlength = 0; register PCRE2_UCHAR *cc = code + 1 + LINK_SIZE; +/* If this is a capturing group, we may have the answer cached, but we can only +use this information if there are no (?| groups in the pattern, because +otherwise group numbers are not unique. */ + +if (*code == OP_CBRA || *code == OP_CBRAPOS || *code == OP_SCBRA || + *code == OP_SCBRAPOS) + { + group = GET2(cc, 0); + cc += IMM2_SIZE; + groupinfo = cb->groupinfo[group]; + if ((cb->external_flags & PCRE2_DUPCAPUSED) == 0) + { + if ((groupinfo & GI_NOT_FIXED_LENGTH) != 0) return FFL_NOTFIXED; + if ((groupinfo & GI_SET_FIXED_LENGTH) != 0) + return groupinfo & GI_FIXED_LENGTH_MASK; + } + } + +/* A large and/or complex regex can take too long to process. This can happen +more often when (?| groups are present in the pattern. */ + +if ((*countptr)++ > 2000) return FFL_TOOCOMPLICATED; + /* Scan along the opcodes for this branch. If we get to the end of the branch, check the length against that of the other branches. */ @@ -833,6 +894,8 @@ for (;;) PCRE2_UCHAR *ce, *cs; register PCRE2_UCHAR op = *cc; + if (branchlength > LOOKBEHIND_MAX) return FFL_TOOLONG; + switch (op) { /* We only need to continue for OP_CBRA (normal capturing bracket) and @@ -845,8 +908,7 @@ for (;;) case OP_ONCE: case OP_ONCE_NC: case OP_COND: - d = find_fixedlength(cc + ((op == OP_CBRA)? IMM2_SIZE : 0), utf, atend, cb, - recurses); + d = find_fixedlength(cc, utf, atend, cb, recurses, countptr); if (d < 0) return d; branchlength += d; do cc += GET(cc, 1); while (*cc == OP_ALT); @@ -865,30 +927,38 @@ for (;;) case OP_ACCEPT: case OP_ASSERT_ACCEPT: if (length < 0) length = branchlength; - else if (length != branchlength) return -1; - if (*cc != OP_ALT) return length; + else if (length != branchlength) goto ISNOTFIXED; + if (*cc != OP_ALT) + { + if (group > 0) + { + groupinfo |= (GI_SET_FIXED_LENGTH | length); + cb->groupinfo[group] = groupinfo; + } + return length; + } cc += 1 + LINK_SIZE; branchlength = 0; break; /* A true recursion implies not fixed length, but a subroutine call may be OK. If the subroutine is a forward reference, we can't deal with - it until the end of the pattern, so return -3. */ + it until the end of the pattern, so return FFL_LATER. */ case OP_RECURSE: - if (!atend) return -3; + if (!atend) return FFL_LATER; cs = ce = (PCRE2_UCHAR *)cb->start_code + GET(cc, 1); /* Start subpattern */ do ce += GET(ce, 1); while (*ce == OP_ALT); /* End subpattern */ - if (cc > cs && cc < ce) return -1; /* Recursion */ + if (cc > cs && cc < ce) goto ISNOTFIXED; /* Recursion */ else /* Check for mutual recursion */ { recurse_check *r = recurses; for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break; - if (r != NULL) return -1; /* Mutual recursion */ + if (r != NULL) goto ISNOTFIXED; /* Mutual recursion */ } this_recurse.prev = recurses; this_recurse.group = cs; - d = find_fixedlength(cs + IMM2_SIZE, utf, atend, cb, &this_recurse); + d = find_fixedlength(cs, utf, atend, cb, &this_recurse, countptr); if (d < 0) return d; branchlength += d; cc += 1 + LINK_SIZE; @@ -1010,7 +1080,7 @@ for (;;) otherwise \C is coded as OP_ALLANY. */ case OP_ANYBYTE: - return -2; + return FFL_BACKSLASHC; /* Check a class for variable quantification */ @@ -1039,12 +1109,12 @@ for (;;) case OP_CRPOSSTAR: case OP_CRPOSPLUS: case OP_CRPOSQUERY: - return -1; + goto ISNOTFIXED; case OP_CRRANGE: case OP_CRMINRANGE: case OP_CRPOSRANGE: - if (GET2(cc,1) != GET2(cc,1+IMM2_SIZE)) return -1; + if (GET2(cc,1) != GET2(cc,1+IMM2_SIZE)) goto ISNOTFIXED; branchlength += (int)GET2(cc,1); cc += 1 + 2 * IMM2_SIZE; break; @@ -1136,16 +1206,24 @@ for (;;) case OP_TYPEUPTO: case OP_UPTO: case OP_UPTOI: - return -1; + goto ISNOTFIXED; /* Catch unrecognized opcodes so that when new ones are added they are not forgotten, as has happened in the past. */ default: - return -4; + return FFL_UNKNOWNOP; } } -/* Control never gets here */ +/* Control never gets here except by goto. */ + +ISNOTFIXED: +if (group > 0) + { + groupinfo |= GI_NOT_FIXED_LENGTH; + cb->groupinfo[group] = groupinfo; + } +return FFL_NOTFIXED; } @@ -1215,30 +1293,72 @@ for (;;) *************************************************/ /* This function scans through a branch of a compiled pattern to see whether it -can match the empty string. It is called from could_be_empty() below and from -compile_branch() when checking for an unlimited repeat of a group that can -match nothing. Note that first_significant_code() skips over backward and -negative forward assertions when its final argument is TRUE. If we hit an -unclosed bracket, we return "empty" - this means we've struck an inner bracket -whose current branch will already have been scanned. +can match the empty string. It is called at the end of compiling to check the +entire pattern, and from compile_branch() when checking for an unlimited repeat +of a group that can match nothing. In the latter case it is called only when +doing the real compile, not during the pre-compile that measures the size of +the compiled pattern. + +Note that first_significant_code() skips over backward and negative forward +assertions when its final argument is TRUE. If we hit an unclosed bracket, we +return "empty" - this means we've struck an inner bracket whose current branch +will already have been scanned. Arguments: code points to start of search endcode points to where to stop utf TRUE if in UTF mode cb compile data + atend TRUE if being called to check an entire pattern recurses chain of recurse_check to catch mutual recursion + countptr pointer to count to catch over-complicated pattern -Returns: TRUE if what is matched could be empty +Returns: 0 if what is matched cannot be empty + 1 if what is matched could be empty + -1 if the pattern is too complicated */ -static BOOL +#define CBE_NOTEMPTY 0 +#define CBE_EMPTY 1 +#define CBE_TOOCOMPLICATED (-1) + + +static int could_be_empty_branch(PCRE2_SPTR code, PCRE2_SPTR endcode, BOOL utf, - compile_block *cb, recurse_check *recurses) + compile_block *cb, BOOL atend, recurse_check *recurses, int *countptr) { +uint32_t group = 0; +uint32_t groupinfo = 0; register PCRE2_UCHAR c; recurse_check this_recurse; +/* If what we are checking has already been set as "could be empty", we know +the answer. */ + +if (*code >= OP_SBRA && *code <= OP_SCOND) return CBE_EMPTY; + +/* If this is a capturing group, we may have the answer cached, but we can only +use this information if there are no (?| groups in the pattern, because +otherwise group numbers are not unique. */ + +if ((cb->external_flags & PCRE2_DUPCAPUSED) == 0 && + (*code == OP_CBRA || *code == OP_CBRAPOS)) + { + group = GET2(code, 1 + LINK_SIZE); + groupinfo = cb->groupinfo[group]; + if ((groupinfo & GI_SET_COULD_BE_EMPTY) != 0) + return ((groupinfo & GI_COULD_BE_EMPTY) != 0)? CBE_EMPTY : CBE_NOTEMPTY; + } + +/* A large and/or complex regex can take too long to process. We have to assume +it can match an empty string. This can happen more often when (?| groups are +present in the pattern and the caching is disabled. Setting the cap at 1100 +allows the test for more than 1023 capturing patterns to work. */ + +if ((*countptr)++ > 1100) return CBE_TOOCOMPLICATED; + +/* Scan the opcodes for this branch. */ + for (code = first_significant_code(code + PRIV(OP_lengths)[*code], TRUE); code < endcode; code = first_significant_code(code + PRIV(OP_lengths)[c], TRUE)) @@ -1257,35 +1377,27 @@ for (code = first_significant_code(code + PRIV(OP_lengths)[*code], TRUE); continue; } - /* For a recursion/subroutine call, if its end has been reached, which - implies a backward reference subroutine call, we can scan it. If it's a - forward reference subroutine call, we can't. To detect forward reference - we have to scan up the list that is kept in the workspace. This function is - called only when doing the real compile, not during the pre-compile that - measures the size of the compiled pattern. */ + /* For a recursion/subroutine call we can scan the recursion when this + function is called at the end, to check a complete pattern. Before then, + recursions just have the group number as their argument and in any case may + be forward references. In that situation, we return CBE_EMPTY, just in case. + It means that unlimited repeats of groups that contain recursions are always + treated as "could be empty" - which just adds a bit more processing time + because of the runtime check. */ if (c == OP_RECURSE) { - PCRE2_SPTR scode = cb->start_code + GET(code, 1); - PCRE2_SPTR endgroup = scode; + PCRE2_SPTR scode, endgroup; BOOL empty_branch; - /* Test for forward reference or uncompleted reference. This is disabled - when called to scan a completed pattern by setting cb->start_workspace to - NULL. */ + if (!atend) goto ISTRUE; + scode = cb->start_code + GET(code, 1); + endgroup = scode; - if (cb->start_workspace != NULL) - { - PCRE2_SPTR tcode; - for (tcode = cb->start_workspace; tcode < cb->hwm; tcode += LINK_SIZE) - if ((int)GET(tcode, 0) == (int)(code + 1 - cb->start_code)) return TRUE; - if (GET(scode, 1) == 0) return TRUE; /* Unclosed */ - } - - /* If the reference is to a completed group, we need to detect whether this - is a recursive call, as otherwise there will be an infinite loop. If it is - a recursion, just skip over it. Simple recursions are easily detected. For - mutual recursions we keep a chain on the stack. */ + /* We need to detect whether this is a recursive call, as otherwise there + will be an infinite loop. If it is a recursion, just skip over it. Simple + recursions are easily detected. For mutual recursions we keep a chain on + the stack. */ do endgroup += GET(endgroup, 1); while (*endgroup == OP_ALT); if (code >= scode && code <= endgroup) continue; /* Simple recursion */ @@ -1297,8 +1409,8 @@ for (code = first_significant_code(code + PRIV(OP_lengths)[*code], TRUE); if (r != NULL) continue; /* Mutual recursion */ } - /* Completed reference; scan the referenced group, remembering it on the - stack chain to detect mutual recursions. */ + /* Scan the referenced group, remembering it on the stack chain to detect + mutual recursions. */ empty_branch = FALSE; this_recurse.prev = recurses; @@ -1306,7 +1418,10 @@ for (code = first_significant_code(code + PRIV(OP_lengths)[*code], TRUE); do { - if (could_be_empty_branch(scode, endcode, utf, cb, &this_recurse)) + int rc = could_be_empty_branch(scode, endcode, utf, cb, atend, + &this_recurse, countptr); + if (rc < 0) return rc; + if (rc > 0) { empty_branch = TRUE; break; @@ -1315,7 +1430,7 @@ for (code = first_significant_code(code + PRIV(OP_lengths)[*code], TRUE); } while (*scode == OP_ALT); - if (!empty_branch) return FALSE; /* All branches are non-empty */ + if (!empty_branch) goto ISFALSE; /* All branches are non-empty */ continue; } @@ -1349,7 +1464,7 @@ for (code = first_significant_code(code + PRIV(OP_lengths)[*code], TRUE); c == OP_COND || c == OP_SCOND) { BOOL empty_branch; - if (GET(code, 1) == 0) return TRUE; /* Hit unclosed bracket */ + if (GET(code, 1) == 0) goto ISTRUE; /* Hit unclosed bracket */ /* If a conditional group has only one branch, there is a second, implied, empty branch, so just skip over the conditional, because it could be empty. @@ -1362,12 +1477,17 @@ for (code = first_significant_code(code + PRIV(OP_lengths)[*code], TRUE); empty_branch = FALSE; do { - if (!empty_branch && could_be_empty_branch(code, endcode, utf, cb, - recurses)) empty_branch = TRUE; + if (!empty_branch) + { + int rc = could_be_empty_branch(code, endcode, utf, cb, atend, + recurses, countptr); + if (rc < 0) return rc; + if (rc > 0) empty_branch = TRUE; + } code += GET(code, 1); } while (*code == OP_ALT); - if (!empty_branch) return FALSE; /* All branches are non-empty */ + if (!empty_branch) goto ISFALSE; /* All branches are non-empty */ } c = *code; @@ -1412,12 +1532,12 @@ for (code = first_significant_code(code + PRIV(OP_lengths)[*code], TRUE); case OP_CRPLUS: /* These repeats aren't empty */ case OP_CRMINPLUS: case OP_CRPOSPLUS: - return FALSE; + goto ISFALSE; case OP_CRRANGE: case OP_CRMINRANGE: case OP_CRPOSRANGE: - if (GET2(ccode, 1) > 0) return FALSE; /* Minimum > 0 */ + if (GET2(ccode, 1) > 0) goto ISFALSE; /* Minimum > 0 */ break; } break; @@ -1474,8 +1594,7 @@ for (code = first_significant_code(code + PRIV(OP_lengths)[*code], TRUE); case OP_TYPEMINPLUS: case OP_TYPEPOSPLUS: case OP_TYPEEXACT: - - return FALSE; + goto ISFALSE; /* These are going to continue, as they may be empty, but we have to fudge the length for the \p and \P cases. */ @@ -1505,7 +1624,7 @@ for (code = first_significant_code(code + PRIV(OP_lengths)[*code], TRUE); case OP_KETRMIN: case OP_KETRPOS: case OP_ALT: - return TRUE; + goto ISTRUE; /* In UTF-8 or UTF-16 mode, STAR, MINSTAR, POSSTAR, QUERY, MINQUERY, POSQUERY, UPTO, MINUPTO, and POSUPTO and their caseless and negative @@ -1579,78 +1698,13 @@ for (code = first_significant_code(code + PRIV(OP_lengths)[*code], TRUE); } } -return TRUE; -} +ISTRUE: +groupinfo |= GI_COULD_BE_EMPTY; +ISFALSE: +if (group > 0) cb->groupinfo[group] = groupinfo | GI_SET_COULD_BE_EMPTY; - -/************************************************* -* Scan compiled regex for non-emptiness * -*************************************************/ - -/* This function is called to check for left recursive calls. We want to check -the current branch of the current pattern to see if it could match the empty -string. If it could, we must look outwards for branches at other levels, -stopping when we pass beyond the bracket which is the subject of the recursion. -This function is called only during the real compile, not during the -pre-compile. - -Arguments: - code points to start of the recursion - endcode points to where to stop (current RECURSE item) - bcptr points to the chain of current (unclosed) branch starts - utf TRUE if in UTF mode - cb compile data - -Returns: TRUE if what is matched could be empty -*/ - -static BOOL -could_be_empty(PCRE2_SPTR code, PCRE2_SPTR endcode, branch_chain *bcptr, - BOOL utf, compile_block *cb) -{ -while (bcptr != NULL && bcptr->current_branch >= code) - { - if (!could_be_empty_branch(bcptr->current_branch, endcode, utf, cb, NULL)) - return FALSE; - bcptr = bcptr->outer; - } -return TRUE; -} - - - -/************************************************* -* Expand the workspace * -*************************************************/ - -/* This function is called during the second compiling phase, if the number of -forward references fills the existing workspace, which is originally a block on -the stack. A larger block is obtained from the heap unless the ultimate limit -has been reached or the increase will be rather small. - -Argument: pointer to the compile data block -Returns: 0 if all went well, else an error number -*/ - -static int -expand_workspace(compile_block *cb) -{ -PCRE2_UCHAR *newspace; -int newsize = cb->workspace_size * 2; -if (newsize > COMPILE_WORK_SIZE_MAX) newsize = COMPILE_WORK_SIZE_MAX; -if (cb->workspace_size >= COMPILE_WORK_SIZE_MAX || - newsize - cb->workspace_size < WORK_SIZE_SAFETY_MARGIN) - return ERR72; -newspace = cb->cx->memctl.malloc(CU2BYTES(newsize), cb->cx->memctl.memory_data); -if (newspace == NULL) return ERR21; -memcpy(newspace, cb->start_workspace, cb->workspace_size * sizeof(PCRE2_UCHAR)); -cb->hwm = (PCRE2_UCHAR *)newspace + (cb->hwm - cb->start_workspace); -if (cb->workspace_size > COMPILE_WORK_SIZE) - cb->cx->memctl.free((void *)cb->start_workspace, cb->cx->memctl.memory_data); -cb->start_workspace = newspace; -cb->workspace_size = newsize; -return 0; +return ((groupinfo & GI_COULD_BE_EMPTY) != 0)? CBE_EMPTY : CBE_NOTEMPTY; } @@ -1697,8 +1751,22 @@ is placed in chptr. A backreference to group n is returned as negative n. On entry, ptr is pointing at the \. On exit, it points the final code unit of the escape sequence. +This function is also called from pcre2_substitute() to handle escape sequences +in replacement strings. In this case, the cb argument is NULL, and only +sequences that define a data character are recognised. The isclass argument is +not relevant, but the options argument is the final value of the compiled +pattern's options. + +There is one "trick" case: when a sequence such as [[:>:]] or \s in UCP mode is +processed, it is replaced by a nested alternative sequence. If this contains a +backslash (which is usually does), ptrend does not point to its end - it still +points to the end of the whole pattern. However, we can detect this case +because cb->nestptr[0] will be non-NULL. The nested sequences are all zero- +terminated and there are only ever two levels of nesting. + Arguments: - ptrptr points to the pattern position pointer + ptrptr points to the input position pointer + ptrend points to the end of the input chptr points to a returned data character errorcodeptr points to the errorcode variable (containing zero) options the current options bits @@ -1711,9 +1779,9 @@ Returns: zero => a data character on error, errorcodeptr is set non-zero */ -static int -check_escape(PCRE2_SPTR *ptrptr, uint32_t *chptr, int *errorcodeptr, - uint32_t options, BOOL isclass, compile_block *cb) +int +PRIV(check_escape)(PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend, uint32_t *chptr, + int *errorcodeptr, uint32_t options, BOOL isclass, compile_block *cb) { BOOL utf = (options & PCRE2_UTF) != 0; PCRE2_SPTR ptr = *ptrptr + 1; @@ -1721,19 +1789,28 @@ register uint32_t c, cc; int escape = 0; int i; +/* Find the end of a nested insert. */ + +if (cb != NULL && cb->nestptr[0] != NULL) + ptrend = ptr + PRIV(strlen)(ptr); + +/* If backslash is at the end of the string, it's an error. */ + +if (ptr >= ptrend) + { + *errorcodeptr = ERR1; + return 0; + } + GETCHARINCTEST(c, ptr); /* Get character value, increment pointer */ ptr--; /* Set pointer back to the last code unit */ -/* If backslash is at the end of the pattern, it's an error. */ - -if (c == CHAR_NULL && ptr >= cb->end_pattern) *errorcodeptr = ERR1; - /* Non-alphanumerics are literals, so we just leave the value in c. An initial value test saves a memory lookup for code points outside the alphanumeric range. Otherwise, do a table lookup. A non-zero result is something that can be returned immediately. Otherwise further processing is required. */ -else if (c < ESCAPES_FIRST || c > ESCAPES_LAST) {} /* Definitely literal */ +if (c < ESCAPES_FIRST || c > ESCAPES_LAST) {} /* Definitely literal */ else if ((i = escapes[c - ESCAPES_FIRST]) != 0) { @@ -1745,7 +1822,9 @@ else if ((i = escapes[c - ESCAPES_FIRST]) != 0) } } -/* Escapes that need further processing, including those that are unknown. */ +/* Escapes that need further processing, including those that are unknown. +When called from pcre2_substitute(), only \c, \o, and \x are recognized (and \u +when BSUX is set). */ else { @@ -1753,6 +1832,15 @@ else BOOL braced, negated, overflow; unsigned int s; + /* Filter calls from pcre2_substitute(). */ + + if (cb == NULL && c != CHAR_c && c != CHAR_o && c != CHAR_x && + (c != CHAR_u || (options & PCRE2_ALT_BSUX) != 0)) + { + *errorcodeptr = ERR3; + return 0; + } + switch (c) { /* A number of Perl escapes are not handled by PCRE. We give an explicit @@ -1884,7 +1972,7 @@ else s = cb->bracount - (s - 1); } - escape = -s; + escape = -(int)s; break; /* The handling of escape sequences consisting of a string of digits @@ -1896,8 +1984,7 @@ else number is less than 10, or if there are that many previous extracting left brackets, it is a back reference. Otherwise, up to three octal digits are read to form an escaped character code. Thus \123 is likely to be octal 123 - (cf \0123, which is octal 012 followed by the literal 3). If the octal - value is greater than 377, the least significant 8 bits are taken. + (cf \0123, which is octal 012 followed by the literal 3). Inside a character class, \ followed by a digit is always either a literal 8 or 9 or an octal number. */ @@ -1909,7 +1996,7 @@ else { oldptr = ptr; /* The integer range is limited by the machine's int representation. */ - s = (int)(c - CHAR_0); + s = c - CHAR_0; overflow = FALSE; while (IS_DIGIT(ptr[1])) { @@ -1927,13 +2014,12 @@ else break; } - /* \1 to \9 are always back references. \8x and \9x are too, unless there - are an awful lot of previous captures; \1x to \7x are octal escapes if - there are not that many previous captures. */ + /* \1 to \9 are always back references. \8x and \9x are too; \1x to \7x + are octal escapes if there are not that many previous captures. */ if (s < 10 || *oldptr >= CHAR_8 || s <= cb->bracount) { - escape = -s; /* Indicates a back reference */ + escape = -(int)s; /* Indicates a back reference */ break; } ptr = oldptr; /* Put the pointer back and fall through */ @@ -1981,7 +2067,7 @@ else #if PCRE2_CODE_UNIT_WIDTH == 32 if (c >= 0x20000000l) { overflow = TRUE; break; } #endif - c = (c << 3) + cc - CHAR_0 ; + c = (c << 3) + (cc - CHAR_0); #if PCRE2_CODE_UNIT_WIDTH == 8 if (c > (utf ? 0x10ffffU : 0xffU)) { overflow = TRUE; break; } #elif PCRE2_CODE_UNIT_WIDTH == 16 @@ -2105,8 +2191,8 @@ else #endif c = *(++ptr); - if (c >= CHAR_a && c <= CHAR_z) c += ESCAPES_UPPER_CASE; - if (c == CHAR_NULL && ptr >= cb->end_pattern) + if (c >= CHAR_a && c <= CHAR_z) c = UPPER_CASE(c); + if (c == CHAR_NULL && ptr >= ptrend) { *errorcodeptr = ERR2; break; @@ -2333,175 +2419,6 @@ return p; -/************************************************* -* Scan compiled regex for specific bracket * -*************************************************/ - -/* This function scans through a compiled pattern until it finds a -capturing bracket with the given number, or, if the number is negative, an -instance of OP_REVERSE for a lookbehind. The function is global in the C sense -so that it can be called from pcre2_study() when finding the minimum matching -length. - -Arguments: - code points to start of expression - utf TRUE in UTF mode - number the required bracket number or negative to find a lookbehind - -Returns: pointer to the opcode for the bracket, or NULL if not found -*/ - -PCRE2_SPTR -PRIV(find_bracket)(PCRE2_SPTR code, BOOL utf, int number) -{ -for (;;) - { - register PCRE2_UCHAR c = *code; - - if (c == OP_END) return NULL; - - /* XCLASS is used for classes that cannot be represented just by a bit map. - This includes negated single high-valued characters. CALLOUT_STR is used for - callouts with string arguments. In both cases the length in the table is - zero; the actual length is stored in the compiled code. */ - - if (c == OP_XCLASS) code += GET(code, 1); - else if (c == OP_CALLOUT_STR) code += GET(code, 1 + 2*LINK_SIZE); - - /* Handle recursion */ - - else if (c == OP_REVERSE) - { - if (number < 0) return (PCRE2_UCHAR *)code; - code += PRIV(OP_lengths)[c]; - } - - /* Handle capturing bracket */ - - else if (c == OP_CBRA || c == OP_SCBRA || - c == OP_CBRAPOS || c == OP_SCBRAPOS) - { - int n = (int)GET2(code, 1+LINK_SIZE); - if (n == number) return (PCRE2_UCHAR *)code; - code += PRIV(OP_lengths)[c]; - } - - /* Otherwise, we can get the item's length from the table, except that for - repeated character types, we have to test for \p and \P, which have an extra - two bytes of parameters, and for MARK/PRUNE/SKIP/THEN with an argument, we - must add in its length. */ - - else - { - switch(c) - { - case OP_TYPESTAR: - case OP_TYPEMINSTAR: - case OP_TYPEPLUS: - case OP_TYPEMINPLUS: - case OP_TYPEQUERY: - case OP_TYPEMINQUERY: - case OP_TYPEPOSSTAR: - case OP_TYPEPOSPLUS: - case OP_TYPEPOSQUERY: - if (code[1] == OP_PROP || code[1] == OP_NOTPROP) code += 2; - break; - - case OP_TYPEUPTO: - case OP_TYPEMINUPTO: - case OP_TYPEEXACT: - case OP_TYPEPOSUPTO: - if (code[1 + IMM2_SIZE] == OP_PROP || code[1 + IMM2_SIZE] == OP_NOTPROP) - code += 2; - break; - - case OP_MARK: - case OP_PRUNE_ARG: - case OP_SKIP_ARG: - case OP_THEN_ARG: - code += code[1]; - break; - } - - /* Add in the fixed length from the table */ - - code += PRIV(OP_lengths)[c]; - - /* In UTF-8 and UTF-16 modes, opcodes that are followed by a character may be - followed by a multi-byte character. The length in the table is a minimum, so - we have to arrange to skip the extra bytes. */ - -#ifdef MAYBE_UTF_MULTI - if (utf) switch(c) - { - case OP_CHAR: - case OP_CHARI: - case OP_NOT: - case OP_NOTI: - case OP_EXACT: - case OP_EXACTI: - case OP_NOTEXACT: - case OP_NOTEXACTI: - case OP_UPTO: - case OP_UPTOI: - case OP_NOTUPTO: - case OP_NOTUPTOI: - case OP_MINUPTO: - case OP_MINUPTOI: - case OP_NOTMINUPTO: - case OP_NOTMINUPTOI: - case OP_POSUPTO: - case OP_POSUPTOI: - case OP_NOTPOSUPTO: - case OP_NOTPOSUPTOI: - case OP_STAR: - case OP_STARI: - case OP_NOTSTAR: - case OP_NOTSTARI: - case OP_MINSTAR: - case OP_MINSTARI: - case OP_NOTMINSTAR: - case OP_NOTMINSTARI: - case OP_POSSTAR: - case OP_POSSTARI: - case OP_NOTPOSSTAR: - case OP_NOTPOSSTARI: - case OP_PLUS: - case OP_PLUSI: - case OP_NOTPLUS: - case OP_NOTPLUSI: - case OP_MINPLUS: - case OP_MINPLUSI: - case OP_NOTMINPLUS: - case OP_NOTMINPLUSI: - case OP_POSPLUS: - case OP_POSPLUSI: - case OP_NOTPOSPLUS: - case OP_NOTPOSPLUSI: - case OP_QUERY: - case OP_QUERYI: - case OP_NOTQUERY: - case OP_NOTQUERYI: - case OP_MINQUERY: - case OP_MINQUERYI: - case OP_NOTMINQUERY: - case OP_NOTMINQUERYI: - case OP_POSQUERY: - case OP_POSQUERYI: - case OP_NOTPOSQUERY: - case OP_NOTPOSQUERYI: - if (HAS_EXTRALEN(code[-1])) code += GET_EXTRALEN(code[-1]); - break; - } -#else - (void)(utf); /* Keep compiler happy by referencing function argument */ -#endif /* MAYBE_UTF_MULTI */ - } - } -} - - - /************************************************* * Scan compiled regex for recursion reference * *************************************************/ @@ -2649,82 +2566,6 @@ for (;;) -/************************************************* -* Adjust OP_RECURSE items in repeated group * -*************************************************/ - -/* OP_RECURSE items contain an offset from the start of the regex to the group -that is referenced. This means that groups can be replicated for fixed -repetition simply by copying (because the recursion is allowed to refer to -earlier groups that are outside the current group). However, when a group is -optional (i.e. the minimum quantifier is zero), OP_BRAZERO or OP_SKIPZERO is -inserted before it, after it has been compiled. This means that any OP_RECURSE -items within it that refer to the group itself or any contained groups have to -have their offsets adjusted. That is one of the jobs of this function. Before -it is called, the partially compiled regex must be temporarily terminated with -OP_END. - -This function has been extended to cope with forward references for recursions -and subroutine calls. It must check the list of such references for the -group we are dealing with. If it finds that one of the recursions in the -current group is on this list, it does not adjust the value in the reference -(which is a group number). After the group has been scanned, all the offsets in -the forward reference list for the group are adjusted. - -Arguments: - group points to the start of the group - adjust the amount by which the group is to be moved - utf TRUE in UTF mode - cb compile data - save_hwm_offset the hwm forward reference offset at the start of the group - -Returns: nothing -*/ - -static void -adjust_recurse(PCRE2_UCHAR *group, int adjust, BOOL utf, compile_block *cb, - size_t save_hwm_offset) -{ -uint32_t offset; -PCRE2_UCHAR *hc; -PCRE2_UCHAR *ptr = group; - -/* Scan the group for recursions. For each one found, check the forward -reference list. */ - -while ((ptr = (PCRE2_UCHAR *)find_recurse(ptr, utf)) != NULL) - { - for (hc = (PCRE2_UCHAR *)cb->start_workspace + save_hwm_offset; hc < cb->hwm; - hc += LINK_SIZE) - { - offset = (int)GET(hc, 0); - if (cb->start_code + offset == ptr + 1) break; - } - - /* If we have not found this recursion on the forward reference list, adjust - the recursion's offset if it's after the start of this group. */ - - if (hc >= cb->hwm) - { - offset = (int)GET(ptr, 1); - if (cb->start_code + offset >= group) PUT(ptr, 1, offset + adjust); - } - - ptr += 1 + LINK_SIZE; - } - -/* Now adjust all forward reference offsets for the group. */ - -for (hc = (PCRE2_UCHAR *)cb->start_workspace + save_hwm_offset; hc < cb->hwm; - hc += LINK_SIZE) - { - offset = (int)GET(hc, 0); - PUT(hc, 0, offset + adjust); - } -} - - - /************************************************* * Check for POSIX class syntax * *************************************************/ @@ -2743,16 +2584,18 @@ didn't consider this to be a POSIX class. Likewise for [:1234:]. The problem in trying to be exactly like Perl is in the handling of escapes. We have to be sure that [abc[:x\]pqr] is *not* treated as containing a POSIX class, but [abc[:x\]pqr:]] is (so that an error can be generated). The code -below handles the special case of \], but does not try to do any other escape -processing. This makes it different from Perl for cases such as [:l\ower:] -where Perl recognizes it as the POSIX class "lower" but PCRE does not recognize -"l\ower". This is a lesser evil than not diagnosing bad classes when Perl does, -I think. +below handles the special cases \\ and \], but does not try to do any other +escape processing. This makes it different from Perl for cases such as +[:l\ower:] where Perl recognizes it as the POSIX class "lower" but PCRE does +not recognize "l\ower". This is a lesser evil than not diagnosing bad classes +when Perl does, I think. A user pointed out that PCRE was rejecting [:a[:digit:]] whereas Perl was not. It seems that the appearance of a nested POSIX class supersedes an apparent external class. For example, [:a[:digit:]b:] matches "a", "b", ":", or -a digit. +a digit. This is handled by returning FALSE if the start of a new group with +the same terminator is encountered, since the next closing sequence must close +the nested group, not the outer one. In Perl, unescaped square brackets may also appear as part of class names. For example, [:a[:abc]b:] gives unknown POSIX class "[:abc]b:]". However, for @@ -2775,22 +2618,18 @@ terminator = *(++ptr); /* compiler warns about "non-constant" initializer. */ for (++ptr; *ptr != CHAR_NULL; ptr++) { - if (*ptr == CHAR_BACKSLASH && ptr[1] == CHAR_RIGHT_SQUARE_BRACKET) ptr++; - else if (*ptr == CHAR_RIGHT_SQUARE_BRACKET) return FALSE; - else + if (*ptr == CHAR_BACKSLASH && + (ptr[1] == CHAR_RIGHT_SQUARE_BRACKET || ptr[1] == CHAR_BACKSLASH)) + ptr++; + else if ((*ptr == CHAR_LEFT_SQUARE_BRACKET && ptr[1] == terminator) || + *ptr == CHAR_RIGHT_SQUARE_BRACKET) return FALSE; + else if (*ptr == terminator && ptr[1] == CHAR_RIGHT_SQUARE_BRACKET) { - if (*ptr == terminator && ptr[1] == CHAR_RIGHT_SQUARE_BRACKET) - { - *endptr = ptr; - return TRUE; - } - if (*ptr == CHAR_LEFT_SQUARE_BRACKET && - (ptr[1] == CHAR_COLON || ptr[1] == CHAR_DOT || - ptr[1] == CHAR_EQUALS_SIGN) && - check_posix_syntax(ptr, endptr)) - return FALSE; + *endptr = ptr; + return TRUE; } } + return FALSE; } @@ -3126,6 +2965,187 @@ return n8; +/************************************************* +* Process (*VERB) name for escapes * +*************************************************/ + +/* This function is called when the PCRE2_ALT_VERBNAMES option is set, to +process the characters in a verb's name argument. It is called twice, once with +codeptr == NULL, to find out the length of the processed name, and again to put +the name into memory. + +Arguments: + ptrptr pointer to the input pointer + codeptr pointer to the compiled code pointer + errorcodeptr pointer to the error code + options the options bits + utf TRUE if processing UTF + cb compile data block + +Returns: length of the processed name, or < 0 on error +*/ + +static int +process_verb_name(PCRE2_SPTR *ptrptr, PCRE2_UCHAR **codeptr, int *errorcodeptr, + uint32_t options, BOOL utf, compile_block *cb) +{ +int32_t arglen = 0; +BOOL inescq = FALSE; +PCRE2_SPTR ptr = *ptrptr; +PCRE2_UCHAR *code = (codeptr == NULL)? NULL : *codeptr; + +for (; ptr < cb->end_pattern; ptr++) + { + uint32_t x = *ptr; + + /* Skip over literals */ + + if (inescq) + { + if (x == CHAR_BACKSLASH && ptr[1] == CHAR_E) + { + inescq = FALSE; + ptr++;; + continue; + } + } + + else /* Not a literal character */ + { + if (x == CHAR_RIGHT_PARENTHESIS) break; + + /* Skip over comments and whitespace in extended mode. */ + + if ((options & PCRE2_EXTENDED) != 0) + { + PCRE2_SPTR wscptr = ptr; + while (MAX_255(x) && (cb->ctypes[x] & ctype_space) != 0) x = *(++ptr); + if (x == CHAR_NUMBER_SIGN) + { + ptr++; + while (*ptr != CHAR_NULL || ptr < cb->end_pattern) + { + if (IS_NEWLINE(ptr)) /* For non-fixed-length newline cases, */ + { /* IS_NEWLINE sets cb->nllen. */ + ptr += cb->nllen; + break; + } + ptr++; +#ifdef SUPPORT_UNICODE + if (utf) FORWARDCHAR(ptr); +#endif + } + } + + /* If we have skipped any characters, restart the loop. */ + + if (ptr > wscptr) + { + ptr--; + continue; + } + } + + /* Process escapes */ + + if (x == '\\') + { + int rc; + *errorcodeptr = 0; + rc = PRIV(check_escape)(&ptr, cb->end_pattern, &x, errorcodeptr, options, + FALSE, cb); + *ptrptr = ptr; /* For possible error */ + if (*errorcodeptr != 0) return -1; + if (rc != 0) + { + if (rc == ESC_Q) + { + inescq = TRUE; + continue; + } + if (rc == ESC_E) continue; + *errorcodeptr = ERR40; + return -1; + } + } + } + + /* We have the next character in the name. */ + +#ifdef SUPPORT_UNICODE + if (utf) + { + if (code == NULL) /* Just want the length */ + { +#if PCRE2_CODE_UNIT_WIDTH == 8 + int i; + for (i = 0; i < PRIV(utf8_table1_size); i++) + if ((int)x <= PRIV(utf8_table1)[i]) break; + arglen += i; +#elif PCRE2_CODE_UNIT_WIDTH == 16 + if (x > 0xffff) arglen++; +#endif + } + else + { + PCRE2_UCHAR cbuff[8]; + x = PRIV(ord2utf)(x, cbuff); + memcpy(code, cbuff, CU2BYTES(x)); + code += x; + } + } + else +#endif /* SUPPORT_UNICODE */ + + /* Not UTF */ + { + if (code != NULL) *code++ = x; + } + + arglen++; + + if ((unsigned int)arglen > MAX_MARK) + { + *errorcodeptr = ERR76; + *ptrptr = ptr; + return -1; + } + } + +/* Update the pointers before returning. */ + +*ptrptr = ptr; +if (codeptr != NULL) *codeptr = code; +return arglen; +} + + + +/************************************************* +* Macro for the next two functions * +*************************************************/ + +/* Both scan_for_captures() and compile_branch() use this macro to generate a +fragment of code that reads the characters of a name and sets its length +(checking for not being too long). Count the characters dynamically, to avoid +the possibility of integer overflow. The same macro is used for reading *VERB +names. */ + +#define READ_NAME(ctype, errno, errset) \ + namelen = 0; \ + while (MAX_255(*ptr) && (cb->ctypes[*ptr] & ctype) != 0) \ + { \ + ptr++; \ + namelen++; \ + if (namelen > MAX_NAME_SIZE) \ + { \ + errset = errno; \ + goto FAILED; \ + } \ + } + + + /************************************************* * Scan regex to identify named groups * *************************************************/ @@ -3157,6 +3177,7 @@ static uint32_t scan_for_captures(PCRE2_SPTR *ptrptr, uint32_t options, compile_block *cb) { uint32_t c; +uint32_t delimiter; uint32_t nest_depth = 0; uint32_t set, unset, *optset; int errorcode = 0; @@ -3165,18 +3186,39 @@ int namelen; int i; BOOL inescq = FALSE; BOOL isdupname; +BOOL skiptoket = FALSE; BOOL utf = (options & PCRE2_UTF) != 0; BOOL negate_class; PCRE2_SPTR name; +PCRE2_SPTR start; PCRE2_SPTR ptr = *ptrptr; named_group *ng; nest_save *top_nest = NULL; nest_save *end_nests = (nest_save *)(cb->start_workspace + cb->workspace_size); +/* The size of the nest_save structure might not be a factor of the size of the +workspace. Therefore we must round down end_nests so as to correctly avoid +creating a nest_save that spans the end of the workspace. */ + +end_nests = (nest_save *)((char *)end_nests - + ((cb->workspace_size * sizeof(PCRE2_UCHAR)) % sizeof(nest_save))); + +/* Now scan the pattern */ + for (; ptr < cb->end_pattern; ptr++) { c = *ptr; + /* Parenthesized groups set skiptoket when all following characters up to the + next closing parenthesis must be ignored. The parenthesis itself must be + processed (to end the nested parenthesized item). */ + + if (skiptoket) + { + if (c != CHAR_RIGHT_PARENTHESIS) continue; + skiptoket = FALSE; + } + /* Skip over literals */ if (inescq) @@ -3226,7 +3268,8 @@ for (; ptr < cb->end_pattern; ptr++) case CHAR_BACKSLASH: errorcode = 0; - escape = check_escape(&ptr, &c, &errorcode, options, FALSE, cb); + escape = PRIV(check_escape)(&ptr, cb->end_pattern, &c, &errorcode, options, + FALSE, cb); if (errorcode != 0) goto FAILED; if (escape == ESC_Q) inescq = TRUE; break; @@ -3238,7 +3281,7 @@ for (; ptr < cb->end_pattern; ptr++) if (PRIV(strncmp_c8)(ptr+1, STRING_WEIRD_STARTWORD, 6) == 0 || PRIV(strncmp_c8)(ptr+1, STRING_WEIRD_ENDWORD, 6) == 0) { - ptr += 7; + ptr += 6; break; } @@ -3273,6 +3316,8 @@ for (; ptr < cb->end_pattern; ptr++) for (;;) { + PCRE2_SPTR tempptr; + if (c == CHAR_NULL && ptr >= cb->end_pattern) { errorcode = ERR6; /* Missing terminating ']' */ @@ -3299,16 +3344,17 @@ for (; ptr < cb->end_pattern; ptr++) } /* Skip POSIX class names. */ - if (c == CHAR_LEFT_SQUARE_BRACKET && (ptr[1] == CHAR_COLON || ptr[1] == CHAR_DOT || - ptr[1] == CHAR_EQUALS_SIGN) && check_posix_syntax(ptr, &ptr)) - ptr ++; - + ptr[1] == CHAR_EQUALS_SIGN) && check_posix_syntax(ptr, &tempptr)) + { + ptr = tempptr + 1; + } else if (c == CHAR_BACKSLASH) { errorcode = 0; - escape = check_escape(&ptr, &c, &errorcode, options, TRUE, cb); + escape = PRIV(check_escape)(&ptr, cb->end_pattern, &c, &errorcode, + options, TRUE, cb); if (errorcode != 0) goto FAILED; if (escape == ESC_Q) inescq = TRUE; } @@ -3326,13 +3372,34 @@ for (; ptr < cb->end_pattern; ptr++) if (ptr[1] != CHAR_QUESTION_MARK) { - if (ptr[1] != CHAR_ASTERISK && - (options & PCRE2_NO_AUTO_CAPTURE) == 0) - cb->bracount++; /* Capturing group */ - else /* (*something) - just skip to closing ket */ + if (ptr[1] != CHAR_ASTERISK) + { + if ((options & PCRE2_NO_AUTO_CAPTURE) == 0) cb->bracount++; + } + + /* (*something) - just skip to closing ket unless PCRE2_ALT_VERBNAMES is + set, in which case we have to process escapes in the string after the + name. */ + + else { ptr += 2; - while (ptr < cb->end_pattern && *ptr != CHAR_RIGHT_PARENTHESIS) ptr++; + while (MAX_255(*ptr) && (cb->ctypes[*ptr] & ctype_word) != 0) ptr++; + if (*ptr == CHAR_COLON) + { + ptr++; + if ((options & PCRE2_ALT_VERBNAMES) != 0) + { + if (process_verb_name(&ptr, NULL, &errorcode, options, utf, cb) < 0) + goto FAILED; + } + else + { + while (ptr < cb->end_pattern && *ptr != CHAR_RIGHT_PARENTHESIS) + ptr++; + } + } + nest_depth--; } } @@ -3342,10 +3409,14 @@ for (; ptr < cb->end_pattern; ptr++) { default: ptr += 2; - if (ptr[0] == CHAR_R || /* (?R) */ - ptr[0] == CHAR_C || /* (?C) */ - IS_DIGIT(ptr[0]) || /* (?n) */ - (ptr[0] == CHAR_MINUS && IS_DIGIT(ptr[1]))) break; /* (?-n) */ + if (ptr[0] == CHAR_R || /* (?R) */ + ptr[0] == CHAR_NUMBER_SIGN || /* (?#) */ + IS_DIGIT(ptr[0]) || /* (?n) */ + (ptr[0] == CHAR_MINUS && IS_DIGIT(ptr[1]))) /* (?-n) */ + { + skiptoket = TRUE; + break; + } /* Handle (?| and (?imsxJU: which are the only other valid forms. Both need a new block on the nest stack. */ @@ -3366,6 +3437,7 @@ for (; ptr < cb->end_pattern; ptr++) top_nest->reset_group = cb->bracount; top_nest->max_group = cb->bracount; top_nest->flags |= NSF_RESET; + cb->external_flags |= PCRE2_DUPCAPUSED; break; } @@ -3420,19 +3492,93 @@ for (; ptr < cb->end_pattern; ptr++) } break; - case CHAR_NUMBER_SIGN: - ptr += 3; - while (ptr < cb->end_pattern && *ptr != CHAR_RIGHT_PARENTHESIS) ptr++; - if (*ptr != CHAR_RIGHT_PARENTHESIS) + /* Skip over a numerical or string argument for a callout. */ + + case CHAR_C: + ptr += 2; + if (ptr[1] == CHAR_RIGHT_PARENTHESIS) break; + if (IS_DIGIT(ptr[1])) { - errorcode = ERR18; + while (IS_DIGIT(ptr[1])) ptr++; + } + + /* Handle a string argument */ + + else + { + ptr++; + delimiter = 0; + for (i = 0; PRIV(callout_start_delims)[i] != 0; i++) + { + if (*ptr == PRIV(callout_start_delims)[i]) + { + delimiter = PRIV(callout_end_delims)[i]; + break; + } + } + + if (delimiter == 0) + { + errorcode = ERR82; + goto FAILED; + } + + start = ptr; + do + { + if (++ptr >= cb->end_pattern) + { + errorcode = ERR81; + ptr = start; /* To give a more useful message */ + goto FAILED; + } + if (ptr[0] == delimiter && ptr[1] == delimiter) ptr += 2; + } + while (ptr[0] != delimiter); + } + + /* Check terminating ) */ + + if (ptr[1] != CHAR_RIGHT_PARENTHESIS) + { + errorcode = ERR39; + ptr++; goto FAILED; } break; + /* Conditional group */ + case CHAR_LEFT_PARENTHESIS: - nest_depth++; - /* Fall through */ + if (ptr[3] != CHAR_QUESTION_MARK) /* Not assertion or callout */ + { + nest_depth++; + ptr += 2; + break; + } + + /* Must be an assertion or a callout */ + + switch(ptr[4]) + { + case CHAR_LESS_THAN_SIGN: + if (ptr[5] != CHAR_EXCLAMATION_MARK && ptr[5] != CHAR_EQUALS_SIGN) + goto MISSING_ASSERTION; + /* Fall through */ + + case CHAR_C: + case CHAR_EXCLAMATION_MARK: + case CHAR_EQUALS_SIGN: + ptr++; + break; + + default: + MISSING_ASSERTION: + ptr += 3; /* To improve error message */ + errorcode = ERR28; + goto FAILED; + } + break; case CHAR_COLON: case CHAR_GREATER_THAN_SIGN: @@ -3486,8 +3632,8 @@ for (; ptr < cb->end_pattern; ptr++) goto FAILED; } - while (MAX_255(*ptr) && (cb->ctypes[*ptr] & ctype_word) != 0) ptr++; - namelen = (int)(ptr - name); + /* Advance ptr, set namelen and check its length. */ + READ_NAME(ctype_word, ERR48, errorcode); if (*ptr != c) { @@ -3502,14 +3648,7 @@ for (; ptr < cb->end_pattern; ptr++) } if (namelen + IMM2_SIZE + 1 > cb->name_entry_size) - { cb->name_entry_size = namelen + IMM2_SIZE + 1; - if (namelen > MAX_NAME_SIZE) - { - errorcode = ERR48; - goto FAILED; - } - } /* We have a valid name for this capturing group. */ @@ -3609,7 +3748,7 @@ for (; ptr < cb->end_pattern; ptr++) if (top_nest == (nest_save *)(cb->start_workspace)) top_nest = NULL; else top_nest--; } - nest_depth--; + if (nest_depth > 0) nest_depth--; /* Can be 0 for unmatched ) */ break; } } @@ -3674,7 +3813,6 @@ int32_t req_caseopt, reqvary, tempreqvary; int after_manual_callout = 0; int escape; size_t length_prevgroup = 0; -size_t item_hwm_offset = 0; register uint32_t c; register PCRE2_UCHAR *code = *codeptr; PCRE2_UCHAR *last_code = code; @@ -3684,7 +3822,6 @@ BOOL inescq = FALSE; BOOL groupsetfirstcu = FALSE; PCRE2_SPTR ptr = *ptrptr; PCRE2_SPTR tempptr; -PCRE2_SPTR nestptr = NULL; PCRE2_UCHAR *previous = NULL; PCRE2_UCHAR *previous_callout = NULL; uint8_t classbits[32]; @@ -3745,6 +3882,7 @@ for (;; ptr++) { BOOL negate_class; BOOL should_flip_negation; + BOOL match_all_or_no_wide_chars; BOOL possessive_quantifier; BOOL is_quantifier; BOOL is_recurse; @@ -3772,12 +3910,14 @@ for (;; ptr++) c = *ptr; /* If we are at the end of a nested substitution, revert to the outer level - string. Nesting only happens one level deep. */ + string. Nesting only happens one or two levels deep, and the inserted string + is always zero terminated. */ - if (c == CHAR_NULL && nestptr != NULL) + if (c == CHAR_NULL && cb->nestptr[0] != NULL) { - ptr = nestptr; - nestptr = NULL; + ptr = cb->nestptr[0]; + cb->nestptr[0] = cb->nestptr[1]; + cb->nestptr[1] = NULL; c = *ptr; } @@ -3789,7 +3929,8 @@ for (;; ptr++) if (code > cb->start_workspace + cb->workspace_size - WORK_SIZE_SAFETY_MARGIN) /* Check for overrun */ { - *errorcodeptr = ERR52; + *errorcodeptr = (code >= cb->start_workspace + cb->workspace_size)? + ERR52 : ERR86; goto FAILED; } @@ -3831,19 +3972,18 @@ for (;; ptr++) last_code = code; } - /* In the real compile phase, just check the workspace used by the forward - reference list. */ + /* Before doing anything else we must handle all the special items that do + nothing, and which may come between an item and its quantifier. Otherwise, + when auto-callouts are enabled, a callout gets incorrectly inserted before + the quantifier is recognized. After recognizing a "do nothing" item, restart + the loop in case another one follows. */ - else if (cb->hwm > cb->start_workspace + cb->workspace_size - - WORK_SIZE_SAFETY_MARGIN) - { - *errorcodeptr = ERR52; - goto FAILED; - } + /* If c is not NULL we are not at the end of the pattern. If it is NULL, we + may still be in the pattern with a NULL data item. In these cases, if we are + in \Q...\E, check for the \E that ends the literal string; if not, we have a + literal character. If not in \Q...\E, an isolated \E is ignored. */ - /* If in \Q...\E, check for the end; if not, we have a literal */ - - if (inescq && (c != CHAR_NULL || ptr < cb->end_pattern)) + if (c != CHAR_NULL || ptr < cb->end_pattern) { if (c == CHAR_BACKSLASH && ptr[1] == CHAR_E) { @@ -3851,7 +3991,7 @@ for (;; ptr++) ptr++; continue; } - else + else if (inescq) /* Literal character */ { if (previous_callout != NULL) { @@ -3866,20 +4006,29 @@ for (;; ptr++) } goto NORMAL_CHAR; } - /* Control does not reach here. */ + + /* Check for the start of a \Q...\E sequence. We must do this here rather + than later in case it is immediately followed by \E, which turns it into a + "do nothing" sequence. */ + + if (c == CHAR_BACKSLASH && ptr[1] == CHAR_Q) + { + inescq = TRUE; + ptr++; + continue; + } } - /* In extended mode, skip white space and comments. We need a loop in order - to check for more white space and more comments after a comment. */ + /* In extended mode, skip white space and #-comments that end at newline. */ if ((options & PCRE2_EXTENDED) != 0) { - for (;;) + PCRE2_SPTR wscptr = ptr; + while (MAX_255(c) && (cb->ctypes[c] & ctype_space) != 0) c = *(++ptr); + if (c == CHAR_NUMBER_SIGN) { - while (MAX_255(c) && (cb->ctypes[c] & ctype_space) != 0) c = *(++ptr); - if (c != CHAR_NUMBER_SIGN) break; ptr++; - while (*ptr != CHAR_NULL) + while (ptr < cb->end_pattern) { if (IS_NEWLINE(ptr)) /* For non-fixed-length newline cases, */ { /* IS_NEWLINE sets cb->nllen. */ @@ -3891,34 +4040,58 @@ for (;; ptr++) if (utf) FORWARDCHAR(ptr); #endif } - c = *ptr; /* Either NULL or the char after a newline */ + } + + /* If we skipped any characters, restart the loop. Otherwise, we didn't see + a comment. */ + + if (ptr > wscptr) + { + ptr--; + continue; } } - /* See if the next thing is a quantifier. */ + /* Skip over (?# comments. */ + + if (c == CHAR_LEFT_PARENTHESIS && ptr[1] == CHAR_QUESTION_MARK && + ptr[2] == CHAR_NUMBER_SIGN) + { + ptr += 3; + while (ptr < cb->end_pattern && *ptr != CHAR_RIGHT_PARENTHESIS) ptr++; + if (*ptr != CHAR_RIGHT_PARENTHESIS) + { + *errorcodeptr = ERR18; + goto FAILED; + } + continue; + } + + /* End of processing "do nothing" items. See if the next thing is a + quantifier. */ is_quantifier = c == CHAR_ASTERISK || c == CHAR_PLUS || c == CHAR_QUESTION_MARK || (c == CHAR_LEFT_CURLY_BRACKET && is_counted_repeat(ptr+1)); - /* Fill in length of a previous callout, except when the next thing is a - quantifier or when processing a property substitution string in UCP mode. */ + /* Fill in length of a previous callout and create an auto callout if + required, except when the next thing is a quantifier or when processing a + property substitution string for \w etc in UCP mode. */ - if (!is_quantifier && previous_callout != NULL && nestptr == NULL && - after_manual_callout-- <= 0) + if (!is_quantifier && cb->nestptr[0] == NULL) { - if (lengthptr == NULL) /* Don't attempt in pre-compile phase */ - complete_callout(previous_callout, ptr, cb); - previous_callout = NULL; - } + if (previous_callout != NULL && after_manual_callout-- <= 0) + { + if (lengthptr == NULL) /* Don't attempt in pre-compile phase */ + complete_callout(previous_callout, ptr, cb); + previous_callout = NULL; + } - /* Create auto callout, except for quantifiers, or while processing property - strings that are substituted for \w etc in UCP mode. */ - - if ((options & PCRE2_AUTO_CALLOUT) != 0 && !is_quantifier && nestptr == NULL) - { - previous_callout = code; - code = auto_callout(code, ptr, cb); + if ((options & PCRE2_AUTO_CALLOUT) != 0) + { + previous_callout = code; + code = auto_callout(code, ptr, cb); + } } /* Process the next pattern item. */ @@ -3982,7 +4155,6 @@ for (;; ptr++) zeroreqcu = reqcu; zeroreqcuflags = reqcuflags; previous = code; - item_hwm_offset = cb->hwm - cb->start_workspace; *code++ = ((options & PCRE2_DOTALL) != 0)? OP_ALLANY: OP_ANY; break; @@ -4008,28 +4180,31 @@ for (;; ptr++) In another (POSIX) regex library, the ugly syntax [[:<:]] and [[:>:]] is used for "start of word" and "end of word". As these are otherwise illegal sequences, we don't break anything by recognizing them. They are replaced - by \b(?=\w) and \b(?<=\w) respectively. Sequences like [a[:<:]] are - erroneous and are handled by the normal code below. */ + by \b(?=\w) and \b(?<=\w) respectively. This can only happen at the top + nesting level, as no other inserted sequences will contains these oddities. + Sequences like [a[:<:]] are erroneous and are handled by the normal code + below. */ case CHAR_LEFT_SQUARE_BRACKET: if (PRIV(strncmp_c8)(ptr+1, STRING_WEIRD_STARTWORD, 6) == 0) { - nestptr = ptr + 7; - ptr = sub_start_of_word - 1; + cb->nestptr[0] = ptr + 7; + ptr = sub_start_of_word; /* Do not combine these statements; clang's */ + ptr--; /* sanitizer moans about a negative index. */ continue; } if (PRIV(strncmp_c8)(ptr+1, STRING_WEIRD_ENDWORD, 6) == 0) { - nestptr = ptr + 7; - ptr = sub_end_of_word - 1; + cb->nestptr[0] = ptr + 7; + ptr = sub_end_of_word; /* Do not combine these statements; clang's */ + ptr--; /* sanitizer moans about a negative index. */ continue; } /* Handle a real character class. */ previous = code; - item_hwm_offset = cb->hwm - cb->start_workspace; /* PCRE supports POSIX class stuff inside a class. Perl gives an error if they are encountered at the top level, so we'll do that too. */ @@ -4079,11 +4254,13 @@ for (;; ptr++) break; } - /* If a class contains a negative special such as \S, we need to flip the - negation flag at the end, so that support for characters > 255 works - correctly (they are all included in the class). */ + /* If a non-extended class contains a negative special such as \S, we need + to flip the negation flag at the end, so that support for characters > 255 + works correctly (they are all included in the class). An extended class may + need to insert specific matching or non-matching code for wide characters. + */ - should_flip_negation = FALSE; + should_flip_negation = match_all_or_no_wide_chars = FALSE; /* Extended class (xclass) will be used when characters > 255 might match. */ @@ -4121,6 +4298,9 @@ for (;; ptr++) for(;;) { PCRE2_SPTR oldptr; +#ifdef EBCDIC + BOOL range_is_literal = TRUE; +#endif if (c == CHAR_NULL && ptr >= cb->end_pattern) { @@ -4203,11 +4383,13 @@ for (;; ptr++) int pc = posix_class + ((local_negate)? POSIX_SUBSIZE/2 : 0); /* The posix_substitutes table specifies which POSIX classes can be - converted to \p or \P items. */ + converted to \p or \P items. This can only happen at top nestling + level, as there will never be a POSIX class in a string that is + substituted for something else. */ if (posix_substitutes[pc] != NULL) { - nestptr = tempptr + 1; + cb->nestptr[0] = tempptr + 1; ptr = posix_substitutes[pc] - 1; goto CONTINUE_CLASS; } @@ -4232,10 +4414,22 @@ for (;; ptr++) ptr = tempptr + 1; goto CONTINUE_CLASS; - /* For all other POSIX classes, no special action is taken in UCP - mode. Fall through to the non_UCP case. */ + /* For the other POSIX classes (ascii, xdigit) we are going to fall + through to the non-UCP case and build a bit map for characters with + code points less than 256. However, if we are in a negated POSIX + class, characters with code points greater than 255 must either all + match or all not match, depending on whether the whole class is not + or is negated. For example, for [[:^ascii:]... they must all match, + whereas for [^[:^xdigit:]... they must not. + + In the special case where there are no xclass items, this is + automatically handled by the use of OP_CLASS or OP_NCLASS, but an + explicit range is needed for OP_XCLASS. Setting a flag here causes + the range to be generated later when it is known that OP_XCLASS is + required. */ default: + match_all_or_no_wide_chars |= local_negate; break; } } @@ -4299,9 +4493,16 @@ for (;; ptr++) if (c == CHAR_BACKSLASH) { - escape = check_escape(&ptr, &ec, errorcodeptr, options, TRUE, cb); + escape = PRIV(check_escape)(&ptr, cb->end_pattern, &ec, errorcodeptr, + options, TRUE, cb); if (*errorcodeptr != 0) goto FAILED; - if (escape == 0) c = ec; /* Escaped single char */ + if (escape == 0) /* Escaped single char */ + { + c = ec; +#ifdef EBCDIC + range_is_literal = FALSE; +#endif + } else if (escape == ESC_b) c = CHAR_BS; /* \b is backspace in a class */ else if (escape == ESC_N) /* \N is not supported in a class */ { @@ -4334,9 +4535,10 @@ for (;; ptr++) case ESC_DU: /* when PCRE2_UCP is set. We replace the */ case ESC_wu: /* escape sequence with an appropriate \p */ case ESC_WU: /* or \P to test Unicode properties instead */ - case ESC_su: /* of the default ASCII testing. */ - case ESC_SU: - nestptr = ptr; + case ESC_su: /* of the default ASCII testing. This might be */ + case ESC_SU: /* a 2nd-level nesting for [[:<:]] or [[:>:]]. */ + cb->nestptr[1] = cb->nestptr[0]; + cb->nestptr[0] = ptr; ptr = substitutes[escape - ESC_DU] - 1; /* Just before substitute */ class_has_8bitchar--; /* Undo! */ break; @@ -4503,9 +4705,12 @@ for (;; ptr++) if (d == CHAR_BACKSLASH) { int descape; - descape = check_escape(&ptr, &d, errorcodeptr, options, TRUE, cb); + descape = PRIV(check_escape)(&ptr, cb->end_pattern, &d, + errorcodeptr, options, TRUE, cb); if (*errorcodeptr != 0) goto FAILED; - +#ifdef EBCDIC + range_is_literal = FALSE; +#endif /* 0 means a character was put into d; \b is backspace; any other special causes an error. */ @@ -4551,9 +4756,48 @@ for (;; ptr++) if (d == CHAR_CR || d == CHAR_NL) cb->external_flags |= PCRE2_HASCRORLF; + /* In an EBCDIC environment, Perl treats alphabetic ranges specially + because there are holes in the encoding, and simply using the range A-Z + (for example) would include the characters in the holes. This applies + only to literal ranges; [\xC1-\xE9] is different to [A-Z]. */ + +#ifdef EBCDIC + if (range_is_literal && + (cb->ctypes[c] & ctype_letter) != 0 && + (cb->ctypes[d] & ctype_letter) != 0 && + (c <= CHAR_z) == (d <= CHAR_z)) + { + uint32_t uc = (c <= CHAR_z)? 0 : 64; + uint32_t C = c - uc; + uint32_t D = d - uc; + + if (C <= CHAR_i) + { + class_has_8bitchar += + add_to_class(classbits, &class_uchardata, options, cb, C + uc, + ((D < CHAR_i)? D : CHAR_i) + uc); + C = CHAR_j; + } + + if (C <= D && C <= CHAR_r) + { + class_has_8bitchar += + add_to_class(classbits, &class_uchardata, options, cb, C + uc, + ((D < CHAR_r)? D : CHAR_r) + uc); + C = CHAR_s; + } + + if (C <= D) + { + class_has_8bitchar += + add_to_class(classbits, &class_uchardata, options, cb, C + uc, + D + uc); + } + } + else +#endif class_has_8bitchar += add_to_class(classbits, &class_uchardata, options, cb, c, d); - goto CONTINUE_CLASS; /* Go get the next char in the class */ } @@ -4566,16 +4810,20 @@ for (;; ptr++) CLASS_SINGLE_CHARACTER: if (class_one_char < 2) class_one_char++; - /* If class_one_char is 1, we have the first single character in the - class, and there have been no prior ranges, or XCLASS items generated by - escapes. If this is the final character in the class, we can optimize by - turning the item into a 1-character OP_CHAR[I] if it's positive, or - OP_NOT[I] if it's negative. In the positive case, it can cause firstcu - to be set. Otherwise, there can be no first char if this item is first, - whatever repeat count may follow. In the case of reqcu, save the - previous value for reinstating. */ + /* If class_one_char is 1 and xclass_has_prop is false, we have the first + single character in the class, and there have been no prior ranges, or + XCLASS items generated by escapes. If this is the final character in the + class, we can optimize by turning the item into a 1-character OP_CHAR[I] + if it's positive, or OP_NOT[I] if it's negative. In the positive case, it + can cause firstcu to be set. Otherwise, there can be no first char if + this item is first, whatever repeat count may follow. In the case of + reqcu, save the previous value for reinstating. */ - if (!inescq && class_one_char == 1 && ptr[1] == CHAR_RIGHT_SQUARE_BRACKET) + if (!inescq && +#ifdef SUPPORT_UNICODE + !xclass_has_prop && +#endif + class_one_char == 1 && ptr[1] == CHAR_RIGHT_SQUARE_BRACKET) { ptr++; zeroreqcu = reqcu; @@ -4636,10 +4884,11 @@ for (;; ptr++) CONTINUE_CLASS: c = *(++ptr); - if (c == 0 && nestptr != NULL) + if (c == CHAR_NULL && cb->nestptr[0] != NULL) { - ptr = nestptr; - nestptr = NULL; + ptr = cb->nestptr[0]; + cb->nestptr[0] = cb->nestptr[1]; + cb->nestptr[1] = NULL; c = *(++ptr); } @@ -4675,21 +4924,36 @@ for (;; ptr++) zeroreqcu = reqcu; zeroreqcuflags = reqcuflags; - /* If there are characters with values > 255, we have to compile an - extended class, with its own opcode, unless there was a negated special - such as \S in the class, and PCRE2_UCP is not set, because in that case all - characters > 255 are in the class, so any that were explicitly given as - well can be ignored. If (when there are explicit characters > 255 that must - be listed) there are no characters < 256, we can omit the bitmap in the - actual compiled code. */ + /* If there are characters with values > 255, or Unicode property settings + (\p or \P), we have to compile an extended class, with its own opcode, + unless there were no property settings and there was a negated special such + as \S in the class, and PCRE2_UCP is not set, because in that case all + characters > 255 are in or not in the class, so any that were explicitly + given as well can be ignored. + + In the UCP case, if certain negated POSIX classes ([:^ascii:] or + [^:xdigit:]) were present in a class, we either have to match or not match + all wide characters (depending on whether the whole class is or is not + negated). This requirement is indicated by match_all_or_no_wide_chars being + true. We do this by including an explicit range, which works in both cases. + + If, when generating an xclass, there are no characters < 256, we can omit + the bitmap in the actual compiled code. */ #ifdef SUPPORT_WIDE_CHARS #ifdef SUPPORT_UNICODE - if (xclass && (!should_flip_negation || (options & PCRE2_UCP) != 0)) + if (xclass && (xclass_has_prop || !should_flip_negation || + (options & PCRE2_UCP) != 0)) #elif PCRE2_CODE_UNIT_WIDTH != 8 - if (xclass && !should_flip_negation) + if (xclass && (xclass_has_prop || !should_flip_negation)) #endif { + if (match_all_or_no_wide_chars) + { + *class_uchardata++ = XCL_RANGE; + class_uchardata += PRIV(ord2utf)(0x100, class_uchardata); + class_uchardata += PRIV(ord2utf)(MAX_UTF_CODE_POINT, class_uchardata); + } *class_uchardata++ = XCL_END; /* Marks the end of extra data */ *code++ = OP_XCLASS; code += LINK_SIZE; @@ -4794,26 +5058,26 @@ for (;; ptr++) if ((options & PCRE2_EXTENDED) != 0) { - PCRE2_SPTR p = ptr + 1; + ptr++; for (;;) { - while (MAX_255(*p) && (cb->ctypes[*p] & ctype_space) != 0) p++; - if (*p != CHAR_NUMBER_SIGN) break; - p++; - while (*p != CHAR_NULL) + while (MAX_255(*ptr) && (cb->ctypes[*ptr] & ctype_space) != 0) ptr++; + if (*ptr != CHAR_NUMBER_SIGN) break; + ptr++; + while (ptr < cb->end_pattern) { - if (IS_NEWLINE(p)) /* For non-fixed-length newline cases, */ + if (IS_NEWLINE(ptr)) /* For non-fixed-length newline cases, */ { /* IS_NEWLINE sets cb->nllen. */ - p += cb->nllen; + ptr += cb->nllen; break; } - p++; + ptr++; #ifdef SUPPORT_UNICODE - if (utf) FORWARDCHAR(p); + if (utf) FORWARDCHAR(ptr); #endif } /* Loop for comment characters */ } /* Loop for multiple comments */ - ptr = p - 1; /* Character before the next significant one. */ + ptr--; /* Last code unit of previous character. */ } /* If the next character is '+', we have a possessive quantifier. This @@ -4835,6 +5099,10 @@ for (;; ptr++) } else repeat_type = greedy_default; + /* If the repeat is {1} we can ignore it. */ + + if (repeat_max == 1 && repeat_min == 1) goto END_REPEAT; + /* If previous was a recursion call, wrap it in atomic brackets so that previous becomes the atomic group. All recursions were so wrapped in the past, but it no longer happens for non-repeated recursions. In fact, the @@ -4850,16 +5118,6 @@ for (;; ptr++) PUT(previous, 3 + 2*LINK_SIZE, 2 + 2*LINK_SIZE); code += 2 + 2 * LINK_SIZE; length_prevgroup = 3 + 3*LINK_SIZE; - - /* When actually compiling, we need to check whether this was a forward - reference, and if so, adjust the offset. */ - - if (lengthptr == NULL && cb->hwm >= cb->start_workspace + LINK_SIZE) - { - int offset = GET(cb->hwm, -LINK_SIZE); - if (offset == previous + 1 - cb->start_code) - PUT(cb->hwm, -LINK_SIZE, offset + 1 + LINK_SIZE); - } } /* Now handle repetition for the different types of item. */ @@ -4888,7 +5146,7 @@ for (;; ptr++) that it's a length rather than a small character. */ #ifdef MAYBE_UTF_MULTI - if (utf && NOT_FIRSTCHAR(code[-1])) + if (utf && NOT_FIRSTCU(code[-1])) { PCRE2_UCHAR *lastchar = code - 1; BACKCHAR(lastchar); @@ -5102,7 +5360,6 @@ for (;; ptr++) { register int i; int len = (int)(code - previous); - size_t base_hwm_offset = item_hwm_offset; PCRE2_UCHAR *bralink = NULL; PCRE2_UCHAR *brazeroptr = NULL; @@ -5150,16 +5407,10 @@ for (;; ptr++) selectively. If the maximum is 1 or unlimited, we just have to stick in the BRAZERO - and do no more at this point. However, we do need to adjust any - OP_RECURSE calls inside the group that refer to the group itself or any - internal or forward referenced group, because the offset is from the - start of the whole regex. Temporarily terminate the pattern while doing - this. */ + and do no more at this point. */ if (repeat_max <= 1) /* Covers 0, 1, and unlimited */ { - *code = OP_END; - adjust_recurse(previous, 1, utf, cb, item_hwm_offset); memmove(previous + 1, previous, CU2BYTES(len)); code++; if (repeat_max == 0) @@ -5176,14 +5427,11 @@ for (;; ptr++) The first one has to be handled carefully because it's the original copy, which has to be moved up. The remainder can be handled by code that is common with the non-zero minimum case below. We have to - adjust the value or repeat_max, since one less copy is required. Once - again, we may have to adjust any OP_RECURSE calls inside the group. */ + adjust the value or repeat_max, since one less copy is required. */ else { int offset; - *code = OP_END; - adjust_recurse(previous, 2 + LINK_SIZE, utf, cb, item_hwm_offset); memmove(previous + 2 + LINK_SIZE, previous, CU2BYTES(len)); code += 2 + LINK_SIZE; *previous++ = OP_BRAZERO + repeat_type; @@ -5202,10 +5450,7 @@ for (;; ptr++) /* If the minimum is greater than zero, replicate the group as many times as necessary, and adjust the maximum to the number of subsequent - copies that we need. If we set a first char from the group, and didn't - set a required char, copy the latter from the former. If there are any - forward reference subroutine calls in the group, there will be entries on - the workspace list; replicate these with an appropriate increment. */ + copies that we need. */ else { @@ -5231,9 +5476,7 @@ for (;; ptr++) } /* This is compiling for real. If there is a set first byte for - the group, and we have not yet set a "required byte", set it. Make - sure there is enough workspace for copying forward references before - doing the copy. */ + the group, and we have not yet set a "required byte", set it. */ else { @@ -5242,29 +5485,9 @@ for (;; ptr++) reqcu = firstcu; reqcuflags = firstcuflags; } - for (i = 1; i < repeat_min; i++) { - PCRE2_UCHAR *hc; - size_t this_hwm_offset = cb->hwm - cb->start_workspace; memcpy(code, previous, CU2BYTES(len)); - - while (cb->hwm > cb->start_workspace + cb->workspace_size - - WORK_SIZE_SAFETY_MARGIN - - (this_hwm_offset - base_hwm_offset)) - { - *errorcodeptr = expand_workspace(cb); - if (*errorcodeptr != 0) goto FAILED; - } - - for (hc = (PCRE2_UCHAR *)cb->start_workspace + base_hwm_offset; - hc < (PCRE2_UCHAR *)cb->start_workspace + this_hwm_offset; - hc += LINK_SIZE) - { - PUT(cb->hwm, 0, GET(hc, 0) + len); - cb->hwm += LINK_SIZE; - } - base_hwm_offset = this_hwm_offset; code += len; } } @@ -5308,9 +5531,6 @@ for (;; ptr++) else for (i = repeat_max - 1; i >= 0; i--) { - PCRE2_UCHAR *hc; - size_t this_hwm_offset = cb->hwm - cb->start_workspace; - *code++ = OP_BRAZERO + repeat_type; /* All but the final copy start a new nesting, maintaining the @@ -5326,26 +5546,6 @@ for (;; ptr++) } memcpy(code, previous, CU2BYTES(len)); - - /* Ensure there is enough workspace for forward references before - copying them. */ - - while (cb->hwm > cb->start_workspace + cb->workspace_size - - WORK_SIZE_SAFETY_MARGIN - - (this_hwm_offset - base_hwm_offset)) - { - *errorcodeptr = expand_workspace(cb); - if (*errorcodeptr != 0) goto FAILED; - } - - for (hc = (PCRE2_UCHAR *)cb->start_workspace + base_hwm_offset; - hc < (PCRE2_UCHAR *)cb->start_workspace + this_hwm_offset; - hc += LINK_SIZE) - { - PUT(cb->hwm, 0, GET(hc, 0) + len + ((i != 0)? 2+LINK_SIZE : 1)); - cb->hwm += LINK_SIZE; - } - base_hwm_offset = this_hwm_offset; code += len; } @@ -5381,7 +5581,7 @@ for (;; ptr++) conditional, we convert the BRA code to the POS form, and the KET code to KETRPOS. (It turns out to be convenient at runtime to detect this kind of subpattern at both the start and at the end.) The use of special opcodes - makes it possible to reduce greatly the stack usage in pcre_exec(). If + makes it possible to reduce greatly the stack usage in pcre2_match(). If the group is preceded by OP_BRAZERO, convert this to OP_BRAPOSZERO. Then, if the minimum number of matches is 1 or 0, cancel the possessive @@ -5411,14 +5611,23 @@ for (;; ptr++) else { - /* In the compile phase, check for empty string matching. */ + /* In the compile phase, check whether the group could match an empty + string. */ if (lengthptr == NULL) { PCRE2_UCHAR *scode = bracode; do { - if (could_be_empty_branch(scode, ketcode, utf, cb, NULL)) + int count = 0; + int rc = could_be_empty_branch(scode, ketcode, utf, cb, FALSE, + NULL, &count); + if (rc < 0) + { + *errorcodeptr = ERR86; + goto FAILED; + } + if (rc > 0) { *bracode += OP_SBRA - OP_BRA; break; @@ -5426,6 +5635,12 @@ for (;; ptr++) scode += GET(scode, 1); } while (*scode == OP_ALT); + + /* A conditional group with only one branch has an implicit empty + alternative branch. */ + + if (*bracode == OP_COND && bracode[GET(bracode,1)] != OP_ALT) + *bracode = OP_SCOND; } /* Handle possessive quantifiers. */ @@ -5434,18 +5649,15 @@ for (;; ptr++) { /* For COND brackets, we wrap the whole thing in a possessively repeated non-capturing bracket, because we have not invented POS - versions of the COND opcodes. Because we are moving code along, we - must ensure that any pending recursive references are updated. */ + versions of the COND opcodes. */ if (*bracode == OP_COND || *bracode == OP_SCOND) { int nlen = (int)(code - bracode); - *code = OP_END; - adjust_recurse(bracode, 1 + LINK_SIZE, utf, cb, item_hwm_offset); memmove(bracode + 1 + LINK_SIZE, bracode, CU2BYTES(nlen)); code += 1 + LINK_SIZE; nlen += 1 + LINK_SIZE; - *bracode = OP_BRAPOS; + *bracode = (*bracode == OP_COND)? OP_BRAPOS : OP_SBRAPOS; *code++ = OP_KETRPOS; PUTINC(code, 0, nlen); PUT(bracode, 1, nlen); @@ -5570,13 +5782,10 @@ for (;; ptr++) *tempcode = opcode_possessify[repcode]; /* For opcode without a special possessified version, wrap the item in - ONCE brackets. Because we are moving code along, we must ensure that - any pending recursive references are updated. */ + ONCE brackets. */ else { - *code = OP_END; - adjust_recurse(tempcode, 1 + LINK_SIZE, utf, cb, item_hwm_offset); memmove(tempcode + 1 + LINK_SIZE, tempcode, CU2BYTES(len)); code += 1 + LINK_SIZE; len += 1 + LINK_SIZE; @@ -5599,33 +5808,19 @@ for (;; ptr++) /* ===================================================================*/ - /* Start of nested parenthesized sub-expression, or comment or lookahead or - lookbehind or option setting or condition or all the other extended - parenthesis forms. We must save the current high-water-mark for the - forward reference list so that we know where they start for this group. - However, because the list may be extended when there are very many forward - references (usually the result of a replicated inner group), we must use - an offset rather than an absolute address. */ + /* Start of nested parenthesized sub-expression, or lookahead or lookbehind + or option setting or condition or all the other extended parenthesis forms. + We must save the current high-water-mark for the forward reference list so + that we know where they start for this group. However, because the list may + be extended when there are very many forward references (usually the result + of a replicated inner group), we must use an offset rather than an absolute + address. Note that (?# comments are dealt with at the top of the loop; + they do not get this far. */ case CHAR_LEFT_PARENTHESIS: ptr++; - /* First deal with comments. Putting this code right at the start ensures - that comments have no bad side effects. */ - - if (ptr[0] == CHAR_QUESTION_MARK && ptr[1] == CHAR_NUMBER_SIGN) - { - ptr += 2; - while (ptr < cb->end_pattern && *ptr != CHAR_RIGHT_PARENTHESIS) ptr++; - if (*ptr != CHAR_RIGHT_PARENTHESIS) - { - *errorcodeptr = ERR18; - goto FAILED; - } - continue; - } - - /* Now deal with various "verbs" that can be introduced by '*'. */ + /* Deal with various "verbs" that can be introduced by '*'. */ if (ptr[0] == CHAR_ASTERISK && (ptr[1] == ':' || (MAX_255(ptr[1]) && ((cb->ctypes[ptr[1]] & ctype_letter) != 0)))) @@ -5637,22 +5832,43 @@ for (;; ptr++) PCRE2_SPTR arg = NULL; previous = NULL; ptr++; - while (MAX_255(*ptr) && (cb->ctypes[*ptr] & ctype_letter) != 0) ptr++; - namelen = (int)(ptr - name); + + /* Increment ptr, set namelen, check length */ + + READ_NAME(ctype_letter, ERR60, *errorcodeptr); /* It appears that Perl allows any characters whatsoever, other than a closing parenthesis, to appear in arguments, so we no longer insist on - letters, digits, and underscores. */ + letters, digits, and underscores. Perl does not, however, do any + interpretation within arguments, and has no means of including a closing + parenthesis. PCRE supports escape processing but only when it is + requested by an option. Note that check_escape() will not return values + greater than the code unit maximum when not in UTF mode. */ if (*ptr == CHAR_COLON) { arg = ++ptr; - while (*ptr != CHAR_NULL && *ptr != CHAR_RIGHT_PARENTHESIS) ptr++; - arglen = (int)(ptr - arg); - if ((unsigned int)arglen > MAX_MARK) + + if ((options & PCRE2_ALT_VERBNAMES) == 0) { - *errorcodeptr = ERR76; - goto FAILED; + arglen = 0; + while (ptr < cb->end_pattern && *ptr != CHAR_RIGHT_PARENTHESIS) + { + ptr++; /* Check length as we go */ + arglen++; /* along, to avoid the */ + if ((unsigned int)arglen > MAX_MARK) /* possibility of overflow. */ + { + *errorcodeptr = ERR76; + goto FAILED; + } + } + } + else + { + /* The length check is in process_verb_names() */ + arglen = process_verb_name(&ptr, NULL, errorcodeptr, options, + utf, cb); + if (arglen < 0) goto FAILED; } } @@ -5697,7 +5913,7 @@ for (;; ptr++) /* Handle other cases with/without an argument */ - else if (arglen == 0) + else if (arglen == 0) /* There is no argument */ { if (verbs[i].op < 0) /* Argument is mandatory */ { @@ -5707,17 +5923,42 @@ for (;; ptr++) setverb = *code++ = verbs[i].op; } - else + else /* An argument is present */ { - if (verbs[i].op_arg < 0) /* Argument is forbidden */ + if (verbs[i].op_arg < 0) /* Argument is forbidden */ { *errorcodeptr = ERR59; goto FAILED; } setverb = *code++ = verbs[i].op_arg; - *code++ = arglen; - memcpy(code, arg, CU2BYTES(arglen)); - code += arglen; + + /* Arguments can be very long, especially in 16- and 32-bit modes, + and can overflow the workspace in the first pass. Instead of + putting the argument into memory, we just update the length counter + and set up an empty argument. */ + + if (lengthptr != NULL) + { + *lengthptr += arglen; + *code++ = 0; + } + else + { + *code++ = arglen; + if ((options & PCRE2_ALT_VERBNAMES) != 0) + { + PCRE2_UCHAR *memcode = code; /* code is "register" */ + (void)process_verb_name(&arg, &memcode, errorcodeptr, options, + utf, cb); + code = memcode; + } + else /* No argument processing */ + { + memcpy(code, arg, CU2BYTES(arglen)); + code += arglen; + } + } + *code++ = 0; } @@ -5911,9 +6152,10 @@ for (;; ptr++) { ptr++; while (IS_DIGIT(*ptr)) minor = minor * 10 + *ptr++ - '0'; + if (minor < 10) minor *= 10; } - if (*ptr != CHAR_RIGHT_PARENTHESIS) + if (*ptr != CHAR_RIGHT_PARENTHESIS || minor > 99) { *errorcodeptr = ERR79; goto FAILED; @@ -5968,6 +6210,12 @@ for (;; ptr++) { while (IS_DIGIT(*ptr)) { + if (recno > INT_MAX / 10 - 1) /* Integer overflow */ + { + while (IS_DIGIT(*ptr)) ptr++; + *errorcodeptr = ERR61; + goto FAILED; + } recno = recno * 10 + (int)(*ptr - CHAR_0); ptr++; } @@ -5992,12 +6240,9 @@ for (;; ptr++) *errorcodeptr = ERR28; /* Assertion expected */ goto FAILED; } - name = ptr++; - while (MAX_255(*ptr) && (cb->ctypes[*ptr] & ctype_word) != 0) - { - ptr++; - } - namelen = (int)(ptr - name); + name = ptr; + /* Increment ptr, set namelen, check length */ + READ_NAME(ctype_word, ERR48, *errorcodeptr); if (lengthptr != NULL) skipunits += IMM2_SIZE; } @@ -6027,7 +6272,7 @@ for (;; ptr++) goto FAILED; } if (refsign != 0) recno = (refsign == CHAR_MINUS)? - cb->bracount - recno + 1 : recno + cb->bracount; + (cb->bracount + 1) - recno : recno + cb->bracount; if (recno <= 0 || (uint32_t)recno > cb->final_bracount) { *errorcodeptr = ERR15; @@ -6103,7 +6348,12 @@ for (;; ptr++) { if (!IS_DIGIT(name[i])) { - *errorcodeptr = ERR15; + *errorcodeptr = ERR15; /* Non-existent subpattern */ + goto FAILED; + } + if (recno > INT_MAX / 10 - 1) /* Integer overflow */ + { + *errorcodeptr = ERR61; goto FAILED; } recno = recno * 10 + name[i] - CHAR_0; @@ -6229,7 +6479,9 @@ for (;; ptr++) } /* During the pre-compile phase, we parse the string and update the - length. There is no need to generate any code. */ + length. There is no need to generate any code. (In fact, the string + has already been parsed in the pre-pass that looks for named + parentheses, but it does no harm to leave this code in.) */ if (lengthptr != NULL) /* Only check the string */ { @@ -6373,8 +6625,8 @@ for (;; ptr++) *errorcodeptr = ERR44; /* Group name must start with non-digit */ goto FAILED; } - while (MAX_255(*ptr) && (cb->ctypes[*ptr] & ctype_word) != 0) ptr++; - namelen = (int)(ptr - name); + /* Increment ptr, set namelen, check length */ + READ_NAME(ctype_word, ERR48, *errorcodeptr); /* In the pre-compile phase, do a syntax check. */ @@ -6390,11 +6642,6 @@ for (;; ptr++) *errorcodeptr = ERR42; goto FAILED; } - if (namelen > MAX_NAME_SIZE) - { - *errorcodeptr = ERR48; - goto FAILED; - } } /* Scan the list of names generated in the pre-pass in order to get @@ -6480,7 +6727,6 @@ for (;; ptr++) if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; previous = code; - item_hwm_offset = cb->hwm - cb->start_workspace; *code++ = ((options & PCRE2_CASELESS) != 0)? OP_DNREFI : OP_DNREF; PUT2INC(code, 0, index); PUT2INC(code, 0, count); @@ -6488,9 +6734,14 @@ for (;; ptr++) /* ------------------------------------------------------------ */ - case CHAR_R: /* Recursion */ - ptr++; /* Same as (?0) */ - /* Fall through */ + case CHAR_R: /* Recursion, same as (?0) */ + recno = 0; + if (*(++ptr) != CHAR_RIGHT_PARENTHESIS) + { + *errorcodeptr = ERR29; + goto FAILED; + } + goto HANDLE_RECURSION; /* ------------------------------------------------------------ */ @@ -6498,7 +6749,6 @@ for (;; ptr++) case CHAR_0: case CHAR_1: case CHAR_2: case CHAR_3: case CHAR_4: case CHAR_5: case CHAR_6: case CHAR_7: case CHAR_8: case CHAR_9: { - PCRE2_SPTR called; terminator = CHAR_RIGHT_PARENTHESIS; /* Come here from the \g<...> and \g'...' code (Oniguruma @@ -6550,7 +6800,7 @@ for (;; ptr++) *errorcodeptr = ERR58; goto FAILED; } - recno = cb->bracount - recno + 1; + recno = (int)(cb->bracount + 1) - recno; if (recno <= 0) { *errorcodeptr = ERR15; @@ -6567,76 +6817,29 @@ for (;; ptr++) recno += cb->bracount; } - /* Come here from code above that handles a named recursion */ + if ((uint32_t)recno > cb->final_bracount) + { + *errorcodeptr = ERR15; + goto FAILED; + } + + /* Come here from code above that handles a named recursion. + We insert the number of the called group after OP_RECURSE. At the + end of compiling the pattern is scanned and these numbers are + replaced by offsets within the pattern. It is done like this to avoid + problems with forward references and adjusting offsets when groups + are duplicated and moved (as discovered in previous implementations). + Note that a recursion does not have a set first character (relevant + if it is repeated, because it will then be wrapped with ONCE + brackets). */ HANDLE_RECURSION: previous = code; - item_hwm_offset = cb->hwm - cb->start_workspace; - called = cb->start_code; - - /* When we are actually compiling, find the bracket that is being - referenced. Temporarily end the regex in case it doesn't exist before - this point. If we end up with a forward reference, first check that - the bracket does occur later so we can give the error (and position) - now. Then remember this forward reference in the workspace so it can - be filled in at the end. */ - - if (lengthptr == NULL) - { - *code = OP_END; - if (recno != 0) - called = PRIV(find_bracket)(cb->start_code, utf, recno); - - /* Forward reference */ - - if (called == NULL) - { - if ((uint32_t)recno > cb->final_bracount) - { - *errorcodeptr = ERR15; - goto FAILED; - } - - /* Fudge the value of "called" so that when it is inserted as an - offset below, what it actually inserted is the reference number - of the group. Then remember the forward reference, expanding the - working space where the list is kept if necessary. */ - - called = cb->start_code + recno; - if (cb->hwm >= cb->start_workspace + cb->workspace_size - - WORK_SIZE_SAFETY_MARGIN) - { - *errorcodeptr = expand_workspace(cb); - if (*errorcodeptr != 0) goto FAILED; - } - PUTINC(cb->hwm, 0, (int)(code + 1 - cb->start_code)); - } - - /* If not a forward reference, and the subpattern is still open, - this is a recursive call. We check to see if this is a left - recursion that could loop for ever, and diagnose that case. We - must not, however, do this check if we are in a conditional - subpattern because the condition might be testing for recursion in - a pattern such as /(?(R)a+|(?R)b)/, which is perfectly valid. - Forever loops are also detected at runtime, so those that occur in - conditional subpatterns will be picked up then. */ - - else if (GET(called, 1) == 0 && cond_depth <= 0 && - could_be_empty(called, code, bcptr, utf, cb)) - { - *errorcodeptr = ERR40; - goto FAILED; - } - } - - /* Insert the recursion/subroutine item. It does not have a set first - character (relevant if it is repeated, because it will then be - wrapped with ONCE brackets). */ - *code = OP_RECURSE; - PUT(code, 1, (int)(called - cb->start_code)); + PUT(code, 1, recno); code += 1 + LINK_SIZE; groupsetfirstcu = FALSE; + cb->had_recurse = TRUE; } /* Can't determine a first byte now */ @@ -6679,44 +6882,16 @@ for (;; ptr++) newoptions = (options | set) & (~unset); /* If the options ended with ')' this is not the start of a nested - group with option changes, so the options change at this level. If this - item is right at the start of the pattern, the options can be - abstracted and made external in the pre-compile phase, and ignored in - the compile phase. This can be helpful when matching -- for instance in - caseless checking of required bytes. - - If the code pointer is not (cb->start_code + 1 + LINK_SIZE), we are - definitely *not* at the start of the pattern because something has been - compiled. In the pre-compile phase, however, the code pointer can have - that value after the start, because it gets reset as code is discarded - during the pre-compile. However, this can happen only at top level - if - we are within parentheses, the starting BRA will still be present. At - any parenthesis level, the length value can be used to test if anything - has been compiled at that level. Thus, a test for both these conditions - is necessary to ensure we correctly detect the start of the pattern in - both phases. - - If we are not at the pattern start, reset the greedy defaults and the - case value for firstcu and reqcu. */ + group with option changes, so the options change at this level. They + must also be passed back for use in subsequent branches. Reset the + greedy defaults and the case value for firstcu and reqcu. */ if (*ptr == CHAR_RIGHT_PARENTHESIS) { - if (code == cb->start_code + 1 + LINK_SIZE && - (lengthptr == NULL || *lengthptr == 2 + 2*LINK_SIZE)) - { - cb->external_options = newoptions; - } - else - { - greedy_default = ((newoptions & PCRE2_UNGREEDY) != 0); - greedy_non_default = greedy_default ^ 1; - req_caseopt = ((newoptions & PCRE2_CASELESS) != 0)? REQ_CASELESS:0; - } - - /* Change options at this level, and pass them back for use - in subsequent branches. */ - *optionsptr = options = newoptions; + greedy_default = ((newoptions & PCRE2_UNGREEDY) != 0); + greedy_non_default = greedy_default ^ 1; + req_caseopt = ((newoptions & PCRE2_CASELESS) != 0)? REQ_CASELESS:0; previous = NULL; /* This item can't be repeated */ continue; /* It is complete */ } @@ -6776,7 +6951,6 @@ for (;; ptr++) else { previous = code; - item_hwm_offset = cb->hwm - cb->start_workspace; } *code = bravalue; @@ -6981,27 +7155,21 @@ for (;; ptr++) are negative the reference number. Only back references and those types that consume a character may be repeated. We can test for values between ESC_b and ESC_Z for the latter; this may have to change if any new ones are - ever created. */ + ever created. + + Note: \Q and \E are handled at the start of the character-processing loop, + not here. */ case CHAR_BACKSLASH: tempptr = ptr; - escape = check_escape(&ptr, &ec, errorcodeptr, options, FALSE, cb); + escape = PRIV(check_escape)(&ptr, cb->end_pattern, &ec, errorcodeptr, + options, FALSE, cb); if (*errorcodeptr != 0) goto FAILED; if (escape == 0) /* The escape coded a single character */ c = ec; else { - if (escape == ESC_Q) /* Handle start of quoted string */ - { - if (ptr[1] == CHAR_BACKSLASH && ptr[2] == CHAR_E) - ptr += 2; /* avoid empty string */ - else inescq = TRUE; - continue; - } - - if (escape == ESC_E) continue; /* Perl ignores an orphan \E */ - /* For metasequences that actually match a character, we disable the setting of a first character if it hasn't already been set. */ @@ -7055,7 +7223,7 @@ for (;; ptr++) if (*p != (PCRE2_UCHAR)terminator) { *errorcodeptr = ERR57; - break; + goto FAILED; } ptr++; goto HANDLE_NUMERICAL_RECURSION; @@ -7070,7 +7238,7 @@ for (;; ptr++) ptr[1] != CHAR_APOSTROPHE && ptr[1] != CHAR_LEFT_CURLY_BRACKET)) { *errorcodeptr = ERR69; - break; + goto FAILED; } is_recurse = FALSE; terminator = (*(++ptr) == CHAR_LESS_THAN_SIGN)? @@ -7092,9 +7260,13 @@ for (;; ptr++) single group (i.e. not to a duplicated name). */ HANDLE_REFERENCE: + if (recno > (int)cb->final_bracount) + { + *errorcodeptr = ERR15; + goto FAILED; + } if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; previous = code; - item_hwm_offset = cb->hwm - cb->start_workspace; *code++ = ((options & PCRE2_CASELESS) != 0)? OP_REFI : OP_REF; PUT2INC(code, 0, recno); cb->backref_map |= (recno < 32)? (1u << recno) : 1; @@ -7124,7 +7296,6 @@ for (;; ptr++) if (!get_ucp(&ptr, &negated, &ptype, &pdata, errorcodeptr, cb)) goto FAILED; previous = code; - item_hwm_offset = cb->hwm - cb->start_workspace; *code++ = ((escape == ESC_p) != negated)? OP_PROP : OP_NOTPROP; *code++ = ptype; *code++ = pdata; @@ -7143,11 +7314,19 @@ for (;; ptr++) /* The use of \C can be locked out. */ +#ifdef NEVER_BACKSLASH_C + else if (escape == ESC_C) + { + *errorcodeptr = ERR85; + goto FAILED; + } +#else else if (escape == ESC_C && (options & PCRE2_NEVER_BACKSLASH_C) != 0) { *errorcodeptr = ERR83; goto FAILED; } +#endif /* For the rest (including \X when Unicode properties are supported), we can obtain the OP value by negating the escape value in the default @@ -7157,13 +7336,15 @@ for (;; ptr++) else { + if (escape == ESC_C) cb->external_flags |= PCRE2_HASBKC; /* Record */ if ((escape == ESC_b || escape == ESC_B || escape == ESC_A) && cb->max_lookbehind == 0) cb->max_lookbehind = 1; #ifdef SUPPORT_UNICODE if (escape >= ESC_DU && escape <= ESC_wu) { - nestptr = ptr + 1; /* Where to resume */ + cb->nestptr[1] = cb->nestptr[0]; /* Back up if at 2nd level */ + cb->nestptr[0] = ptr + 1; /* Where to resume */ ptr = substitutes[escape - ESC_DU] - 1; /* Just before substitute */ } else @@ -7173,7 +7354,6 @@ for (;; ptr++) { previous = (escape > ESC_b && escape < ESC_Z)? code : NULL; - item_hwm_offset = cb->hwm - cb->start_workspace; *code++ = (!utf && escape == ESC_C)? OP_ALLANY : escape; } } @@ -7208,7 +7388,6 @@ for (;; ptr++) ONE_CHAR: previous = code; - item_hwm_offset = cb->hwm - cb->start_workspace; /* For caseless UTF mode, check whether this character has more than one other case. If so, generate a special OP_PROP item instead of OP_CHARI. */ @@ -7350,7 +7529,6 @@ int32_t firstcuflags, reqcuflags; uint32_t branchfirstcu, branchreqcu; int32_t branchfirstcuflags, branchreqcuflags; size_t length; -size_t save_hwm_offset; unsigned int orig_bracount; unsigned int max_bracount; branch_chain bc; @@ -7372,8 +7550,6 @@ bc.current_branch = code; firstcu = reqcu = 0; firstcuflags = reqcuflags = REQ_UNSET; -save_hwm_offset = cb->hwm - cb->start_workspace; /* hwm at start of group */ - /* Accumulate the length for use in the pre-compile phase. Start with the length of the BRA and KET and any extra code units that are required at the beginning. We accumulate in a local variable to save frequent testing of @@ -7508,26 +7684,26 @@ for (;;) /* If lookbehind, check that this branch matches a fixed-length string, and put the length into the OP_REVERSE item. Temporarily mark the end of the - branch with OP_END. If the branch contains OP_RECURSE, the result is -3 - because there may be forward references that we can't check here. Set a - flag to cause another lookbehind check at the end. Why not do it all at the - end? Because common, erroneous checks are picked up here and the offset of - the problem can be shown. */ + branch with OP_END. If the branch contains OP_RECURSE, the result is + FFL_LATER (a negative value) because there may be forward references that + we can't check here. Set a flag to cause another lookbehind check at the + end. Why not do it all at the end? Because common errors can be picked up + here and the offset of the problem can be shown. */ if (lookbehind) { int fixed_length; + int count = 0; *code = OP_END; fixed_length = find_fixedlength(last_branch, (options & PCRE2_UTF) != 0, - FALSE, cb, NULL); - if (fixed_length == -3) + FALSE, cb, NULL, &count); + if (fixed_length == FFL_LATER) { cb->check_lookbehind = TRUE; } else if (fixed_length < 0) { - *errorcodeptr = (fixed_length == -2)? ERR36 : - (fixed_length == -4)? ERR70: ERR25; + *errorcodeptr = fixed_length_errors[-fixed_length]; *ptrptr = ptr; return FALSE; } @@ -7569,18 +7745,13 @@ for (;;) code += 1 + LINK_SIZE; /* If it was a capturing subpattern, check to see if it contained any - recursive back references. If so, we must wrap it in atomic brackets. - Because we are moving code along, we must ensure that any pending recursive - or forward subroutine references are updated. In any event, remove the - block from the chain. */ + recursive back references. If so, we must wrap it in atomic brackets. In + any event, remove the block from the chain. */ if (capnumber > 0) { if (cb->open_caps->flag) { - *code = OP_END; - adjust_recurse(start_bracket, 1 + LINK_SIZE, - (options & PCRE2_UTF) != 0, cb, save_hwm_offset); memmove(start_bracket + 1 + LINK_SIZE, start_bracket, CU2BYTES(code - start_bracket)); *start_bracket = OP_ONCE; @@ -8051,7 +8222,7 @@ a pointer to a block of store holding a compiled version of the expression. Arguments: pattern the regular expression - patlen the length of the pattern, or < 0 for zero-terminated + patlen the length of the pattern, or PCRE2_ZERO_TERMINATED options option bits errorptr pointer to errorcode erroroffset pointer to error offset @@ -8097,10 +8268,11 @@ PCRE2_UCHAR stack_copied_pattern[COPIED_PATTERN_SIZE]; named_group named_groups[NAMED_GROUP_LIST_SIZE]; /* The workspace is used in different ways in the different compiling phases. -Ensure that it is 16-bit aligned for the preliminary group scan. */ +It needs to be 16-bit aligned for the preliminary group scan, and 32-bit +aligned for the group information cache. */ -uint16_t c16workspace[(COMPILE_WORK_SIZE * sizeof(PCRE2_UCHAR))/sizeof(uint16_t)]; -PCRE2_UCHAR *cworkspace = (PCRE2_UCHAR *)c16workspace; +uint32_t c32workspace[C32_WORK_SIZE]; +PCRE2_UCHAR *cworkspace = (PCRE2_UCHAR *)c32workspace; /* -------------- Check arguments and set up the pattern ----------------- */ @@ -8135,10 +8307,24 @@ if (ccontext == NULL) /* A zero-terminated pattern is indicated by the special length value PCRE2_ZERO_TERMINATED. Otherwise, we make a copy of the pattern and add a zero, to ensure that it is always possible to look one code unit beyond the end of -the pattern's characters. */ +the pattern's characters. In both cases, check that the pattern is overlong. */ -if (patlen == PCRE2_ZERO_TERMINATED) patlen = PRIV(strlen)(pattern); else +if (patlen == PCRE2_ZERO_TERMINATED) { + patlen = PRIV(strlen)(pattern); + if (patlen > ccontext->max_pattern_length) + { + *errorptr = ERR88; + return NULL; + } + } +else + { + if (patlen > ccontext->max_pattern_length) + { + *errorptr = ERR88; + return NULL; + } if (patlen < COPIED_PATTERN_SIZE) copied_pattern = stack_copied_pattern; else @@ -8170,9 +8356,11 @@ cb.bracount = cb.final_bracount = 0; cb.cx = ccontext; cb.dupnames = FALSE; cb.end_pattern = pattern + patlen; +cb.nestptr[0] = cb.nestptr[1] = NULL; cb.external_flags = 0; cb.external_options = options; -cb.hwm = cworkspace; +cb.groupinfo = c32workspace; +cb.had_recurse = FALSE; cb.iscondassert = FALSE; cb.max_lookbehind = 0; cb.name_entry_size = 0; @@ -8240,14 +8428,21 @@ while (ptr[skipatstart] == CHAR_LEFT_PARENTHESIS && case PSO_LIMR: c = 0; pp = skipatstart; + if (!IS_DIGIT(ptr[pp])) + { + errorcode = ERR60; + ptr += pp; + goto HAD_ERROR; + } while (IS_DIGIT(ptr[pp])) { if (c > UINT32_MAX / 10 - 1) break; /* Integer overflow */ - c = c*10 + ptr[pp++] - CHAR_0; + c = c*10 + (ptr[pp++] - CHAR_0); } if (ptr[pp++] != CHAR_RIGHT_PARENTHESIS) { errorcode = ERR60; + ptr += pp; goto HAD_ERROR; } if (p->type == PSO_LIMM) limit_match = c; @@ -8288,7 +8483,7 @@ if (utf) } if ((options & PCRE2_NO_UTF_CHECK) == 0 && (errorcode = PRIV(valid_utf)(pattern, patlen, erroroffset)) != 0) - goto HAD_ERROR; + goto HAD_UTF_ERROR; } /* Check UCP lockout. */ @@ -8424,7 +8619,7 @@ re->first_codeunit = 0; re->last_codeunit = 0; re->bsr_convention = bsr; re->newline_convention = newline; -re->max_lookbehind = +re->max_lookbehind = 0; re->minlength = 0; re->top_bracket = 0; re->top_backref = 0; @@ -8437,12 +8632,32 @@ code follows after that. */ codestart = (PCRE2_SPTR)((uint8_t *)re + sizeof(pcre2_real_code)) + re->name_entry_size * re->name_count; +/* Workspace is needed to remember information about numbered groups: whether a +group can match an empty string and what its fixed length is. This is done to +avoid the possibility of recursive references causing very long compile times +when checking these features. Unnumbered groups do not have this exposure since +they cannot be referenced. We use an indexed vector for this purpose. If there +are sufficiently few groups, it can be the c32workspace vector, as set up +above. Otherwise we have to get/free a special vector. The vector must be +initialized to zero. */ + +if (cb.final_bracount >= C32_WORK_SIZE) + { + cb.groupinfo = ccontext->memctl.malloc( + (cb.final_bracount + 1)*sizeof(uint32_t), ccontext->memctl.memory_data); + if (cb.groupinfo == NULL) + { + errorcode = ERR21; + goto HAD_ERROR; + } + } +memset(cb.groupinfo, 0, (cb.final_bracount + 1) * sizeof(uint32_t)); + /* Update the compile data block for the actual compile. The starting points of the name/number translation table and of the code are passed around in the compile data block. The start/end pattern and initial options are already set from the pre-compile phase, as is the name_entry_size field. Reset the bracket -count and the names_found field. Also reset the hwm field; this time it's used -for remembering forward references to subpatterns. */ +count and the names_found field. */ cb.parens_depth = 0; cb.assert_depth = 0; @@ -8450,7 +8665,6 @@ cb.bracount = 0; cb.max_lookbehind = 0; cb.name_table = (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code)); cb.start_code = codestart; -cb.hwm = (PCRE2_UCHAR *)(cb.start_workspace); cb.iscondassert = FALSE; cb.req_varyopt = 0; cb.had_accept = FALSE; @@ -8508,6 +8722,65 @@ if (usedlength > length) errorcode = ERR23; else #endif } +/* Scan the pattern for recursion/subroutine calls and convert the group +numbers into offsets. Maintain a small cache so that repeated groups containing +recursions are efficiently handled. */ + +#define RSCAN_CACHE_SIZE 8 + +if (errorcode == 0 && cb.had_recurse) + { + PCRE2_UCHAR *rcode; + PCRE2_SPTR rgroup; + int ccount = 0; + int start = RSCAN_CACHE_SIZE; + recurse_cache rc[RSCAN_CACHE_SIZE]; + + for (rcode = (PCRE2_UCHAR *)find_recurse(codestart, utf); + rcode != NULL; + rcode = (PCRE2_UCHAR *)find_recurse(rcode + 1 + LINK_SIZE, utf)) + { + int i, p, recno; + + recno = (int)GET(rcode, 1); + if (recno == 0) rgroup = codestart; else + { + PCRE2_SPTR search_from = codestart; + rgroup = NULL; + for (i = 0, p = start; i < ccount; i++, p = (p + 1) & 7) + { + if (recno == rc[p].recno) + { + rgroup = rc[p].group; + break; + } + + /* Group n+1 must always start to the right of group n, so we can save + search time below when the new group number is greater than any of the + previously found groups. */ + + if (recno > rc[p].recno) search_from = rc[p].group; + } + + if (rgroup == NULL) + { + rgroup = PRIV(find_bracket)(search_from, utf, recno); + if (rgroup == NULL) + { + errorcode = ERR53; + break; + } + if (--start < 0) start = RSCAN_CACHE_SIZE - 1; + rc[start].recno = recno; + rc[start].group = rgroup; + if (ccount < RSCAN_CACHE_SIZE) ccount++; + } + } + + PUT(rcode, 1, rgroup - codestart); + } + } + /* In rare debugging situations we sometimes need to look at the compiled code at this stage. */ @@ -8516,37 +8789,6 @@ pcre2_printint(re, stderr, TRUE); fprintf(stderr, "Length=%lu Used=%lu\n", length, usedlength); #endif -/* Fill in any forward references that are required. There may be repeated -references; optimize for them, as searching a large regex takes time. The -test of errorcode inside the loop means that nothing is done if it is already -non-zero. */ - -if (cb.hwm > cb.start_workspace) - { - int prev_recno = -1; - PCRE2_SPTR groupptr = NULL; - while (errorcode == 0 && cb.hwm > cb.start_workspace) - { - int offset, recno; - cb.hwm -= LINK_SIZE; - offset = GET(cb.hwm, 0); - recno = GET(codestart, offset); - if (recno != prev_recno) - { - groupptr = PRIV(find_bracket)(codestart, utf, recno); - prev_recno = recno; - } - if (groupptr == NULL) errorcode = ERR53; - else PUT(((PCRE2_UCHAR *)codestart), offset, (int)(groupptr - codestart)); - } - } - -/* If the workspace had to be expanded, free the new memory. */ - -if (cb.workspace_size > COMPILE_WORK_SIZE) - ccontext->memctl.free((void *)cb.start_workspace, - ccontext->memctl.memory_data); - /* After a successful compile, give an error if there's back reference to a non-existent capturing subpattern. Then, unless disabled, check whether any single character iterators can be auto-possessified. The function overwrites @@ -8558,7 +8800,7 @@ compiler gives a warning about loss of "const" attribute if the cast if (errorcode == 0) { if (re->top_backref > re->top_bracket) errorcode = ERR15; - else if ((options & PCRE2_NO_AUTO_POSSESS) == 0) + else if ((re->overall_options & PCRE2_NO_AUTO_POSSESS) == 0) { PCRE2_UCHAR *temp = (PCRE2_UCHAR *)codestart; if (PRIV(auto_possessify)(temp, utf, &cb) != 0) errorcode = ERR80; @@ -8591,15 +8833,15 @@ if (errorcode == 0 && cb.check_lookbehind) if (GET(cc, 1) == 0) { int fixed_length; + int count = 0; PCRE2_UCHAR *be = cc - 1 - LINK_SIZE + GET(cc, -LINK_SIZE); int end_op = *be; *be = OP_END; - fixed_length = find_fixedlength(cc, utf, TRUE, &cb, NULL); + fixed_length = find_fixedlength(cc, utf, TRUE, &cb, NULL, &count); *be = end_op; if (fixed_length < 0) { - errorcode = (fixed_length == -2)? ERR36 : - (fixed_length == -4)? ERR70 : ERR25; + errorcode = fixed_length_errors[-fixed_length]; break; } if (fixed_length > cb.max_lookbehind) cb.max_lookbehind = fixed_length; @@ -8607,6 +8849,13 @@ if (errorcode == 0 && cb.check_lookbehind) } cc += 1 + LINK_SIZE; } + + /* The previous value of the maximum lookbehind was transferred to the + compiled regex block above. We could have updated this value in the loop + above, but keep the two values in step, just in case some later code below + uses the cb value. */ + + re->max_lookbehind = cb.max_lookbehind; } /* Failed to compile, or error while post-processing. Earlier errors get here @@ -8615,10 +8864,11 @@ via the dreaded goto. */ if (errorcode != 0) { HAD_ERROR: + *erroroffset = (int)(ptr - pattern); + HAD_UTF_ERROR: + *errorptr = errorcode; pcre2_code_free(re); re = NULL; - *errorptr = errorcode; - *erroroffset = (int)(ptr - pattern); goto EXIT; } @@ -8712,7 +8962,14 @@ can be provided to applications. */ do { - if (could_be_empty_branch(codestart, code, utf, &cb, NULL)) + int count = 0; + int rc = could_be_empty_branch(codestart, code, utf, &cb, TRUE, NULL, &count); + if (rc < 0) + { + errorcode = ERR86; + goto HAD_ERROR; + } + if (rc > 0) { re->flags |= PCRE2_MATCH_EMPTY; break; @@ -8734,13 +8991,16 @@ if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0 && /* Control ends up here in all cases. If memory was obtained for a zero-terminated copy of the pattern, remember to free it before returning. Also -free the list of named groups if a larger one had to be obtained. */ +free the list of named groups if a larger one had to be obtained, and likewise +the group information vector. */ EXIT: if (copied_pattern != stack_copied_pattern) ccontext->memctl.free(copied_pattern, ccontext->memctl.memory_data); if (cb.named_group_list_size > NAMED_GROUP_LIST_SIZE) ccontext->memctl.free((void *)cb.named_groups, ccontext->memctl.memory_data); +if (cb.groupinfo != c32workspace) + ccontext->memctl.free((void *)cb.groupinfo, ccontext->memctl.memory_data); return re; /* Will be NULL after an error */ } diff --git a/pcre2-10.20/src/pcre2_config.c b/pcre2-10.21/src/pcre2_config.c similarity index 99% rename from pcre2-10.20/src/pcre2_config.c rename to pcre2-10.21/src/pcre2_config.c index 22aa3587d..921845949 100644 --- a/pcre2-10.20/src/pcre2_config.c +++ b/pcre2-10.21/src/pcre2_config.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2014 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without diff --git a/pcre2-10.20/src/pcre2_context.c b/pcre2-10.21/src/pcre2_context.c similarity index 91% rename from pcre2-10.20/src/pcre2_context.c rename to pcre2-10.21/src/pcre2_context.c index 6146999df..ae050fe92 100644 --- a/pcre2-10.20/src/pcre2_context.c +++ b/pcre2-10.21/src/pcre2_context.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2014 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -131,13 +131,14 @@ return gcontext; when no context is supplied to the compile function. */ const pcre2_compile_context PRIV(default_compile_context) = { - { default_malloc, default_free, NULL }, - NULL, - NULL, - PRIV(default_tables), - BSR_DEFAULT, - NEWLINE_DEFAULT, - PARENS_NEST_LIMIT }; + { default_malloc, default_free, NULL }, /* Default memory handling */ + NULL, /* Stack guard */ + NULL, /* Stack guard data */ + PRIV(default_tables), /* Character tables */ + PCRE2_UNSET, /* Max pattern length */ + BSR_DEFAULT, /* Backslash R default */ + NEWLINE_DEFAULT, /* Newline convention */ + PARENS_NEST_LIMIT }; /* As it says */ /* The create function copies the default into the new memory, but must override the default memory handling functions if a gcontext was provided. */ @@ -169,6 +170,7 @@ const pcre2_match_context PRIV(default_match_context) = { #endif NULL, NULL, + PCRE2_UNSET, /* Offset limit */ MATCH_LIMIT, MATCH_LIMIT_RECURSION }; @@ -294,6 +296,13 @@ switch(value) } } +PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION +pcre2_set_max_pattern_length(pcre2_compile_context *ccontext, PCRE2_SIZE length) +{ +ccontext->max_pattern_length = length; +return 0; +} + PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_newline(pcre2_compile_context *ccontext, uint32_t newline) { @@ -347,6 +356,13 @@ mcontext->match_limit = limit; return 0; } +PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION +pcre2_set_offset_limit(pcre2_match_context *mcontext, PCRE2_SIZE limit) +{ +mcontext->offset_limit = limit; +return 0; +} + PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_recursion_limit(pcre2_match_context *mcontext, uint32_t limit) { diff --git a/pcre2-10.20/src/pcre2_dfa_match.c b/pcre2-10.21/src/pcre2_dfa_match.c similarity index 97% rename from pcre2-10.20/src/pcre2_dfa_match.c rename to pcre2-10.21/src/pcre2_dfa_match.c index b14477def..76bc08525 100644 --- a/pcre2-10.20/src/pcre2_dfa_match.c +++ b/pcre2-10.21/src/pcre2_dfa_match.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2014 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -433,13 +433,13 @@ move back, and set up each alternative appropriately. */ if (*first_op == OP_REVERSE) { - int max_back = 0; - int gone_back; + size_t max_back = 0; + size_t gone_back; end_code = this_start_code; do { - int back = GET(end_code, 2+LINK_SIZE); + size_t back = GET(end_code, 2+LINK_SIZE); if (back > max_back) max_back = back; end_code += GET(end_code, 1); } @@ -466,8 +466,8 @@ if (*first_op == OP_REVERSE) /* In byte-mode we can do this quickly. */ { - gone_back = (current_subject - max_back < start_subject)? - (int)(current_subject - start_subject) : max_back; + size_t current_offset = (size_t)(current_subject - start_subject); + gone_back = (current_offset < max_back)? current_offset : max_back; current_subject -= gone_back; } @@ -481,7 +481,7 @@ if (*first_op == OP_REVERSE) end_code = this_start_code; do { - int back = GET(end_code, 2+LINK_SIZE); + size_t back = GET(end_code, 2+LINK_SIZE); if (back <= gone_back) { int bstate = (int)(end_code - start_code + 2 + 2*LINK_SIZE); @@ -2774,7 +2774,7 @@ for (;;) { PCRE2_SPTR p = start_subject + local_offsets[rc]; PCRE2_SPTR pp = start_subject + local_offsets[rc+1]; - while (p < pp) if (NOT_FIRSTCHAR(*p++)) charcount--; + while (p < pp) if (NOT_FIRSTCU(*p++)) charcount--; } #endif if (charcount > 0) @@ -2874,7 +2874,7 @@ for (;;) PCRE2_SPTR pp = local_ptr; charcount = (int)(pp - p); #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 - if (utf) while (p < pp) if (NOT_FIRSTCHAR(*p++)) charcount--; + if (utf) while (p < pp) if (NOT_FIRSTCU(*p++)) charcount--; #endif ADD_NEW_DATA(-next_state_offset, 0, (charcount - 1)); } @@ -2960,7 +2960,7 @@ for (;;) { PCRE2_SPTR p = start_subject + local_offsets[0]; PCRE2_SPTR pp = start_subject + local_offsets[1]; - while (p < pp) if (NOT_FIRSTCHAR(*p++)) charcount--; + while (p < pp) if (NOT_FIRSTCU(*p++)) charcount--; } #endif ADD_NEW_DATA(-next_state_offset, 0, (charcount - 1)); @@ -3116,6 +3116,7 @@ const pcre2_real_code *re = (const pcre2_real_code *)code; PCRE2_SPTR start_match; PCRE2_SPTR end_subject; +PCRE2_SPTR bumpalong_limit; PCRE2_SPTR req_cu_ptr; BOOL utf, anchored, startline, firstline; @@ -3172,15 +3173,10 @@ occur. */ #define FF (PCRE2_NOTEMPTY_SET|PCRE2_NE_ATST_SET) #define OO (PCRE2_NOTEMPTY|PCRE2_NOTEMPTY_ATSTART) -options |= (re->flags & FF) / ((FF & -FF) / (OO & -OO)); +options |= (re->flags & FF) / ((FF & (~FF+1)) / (OO & (~OO+1))); #undef FF #undef OO -/* A NULL match context means "use a default context" */ - -if (mcontext == NULL) - mcontext = (pcre2_match_context *)(&PRIV(default_match_context)); - /* If restarting after a partial match, do some sanity checks on the contents of the workspace. */ @@ -3205,8 +3201,11 @@ where to start. */ startline = (re->flags & PCRE2_STARTLINE) != 0; firstline = (re->overall_options & PCRE2_FIRSTLINE) != 0; +bumpalong_limit = end_subject; -/* Fill in the fields in the match block. */ +/* Get data from the match context, if present, and fill in the fields in the +match block. It is an error to set an offset limit without setting the flag at +compile time. */ if (mcontext == NULL) { @@ -3215,6 +3214,12 @@ if (mcontext == NULL) } else { + if (mcontext->offset_limit != PCRE2_UNSET) + { + if ((re->overall_options & PCRE2_USE_OFFSET_LIMIT) == 0) + return PCRE2_ERROR_BADOFFSETLIMIT; + bumpalong_limit = subject + mcontext->offset_limit; + } mb->callout = mcontext->callout; mb->callout_data = mcontext->callout_data; mb->memctl = mcontext->memctl; @@ -3264,18 +3269,50 @@ switch(re->newline_convention) /* Check a UTF string for validity if required. For 8-bit and 16-bit strings, we must also check that a starting offset does not point into the middle of a -multiunit character. */ +multiunit character. We check only the portion of the subject that is going to +be inspected during matching - from the offset minus the maximum back reference +to the given length. This saves time when a small part of a large subject is +being matched by the use of a starting offset. Note that the maximum lookbehind +is a number of characters, not code units. */ #ifdef SUPPORT_UNICODE if (utf && (options & PCRE2_NO_UTF_CHECK) == 0) { - match_data->rc = PRIV(valid_utf)(subject, length, &(match_data->startchar)); - if (match_data->rc != 0) return match_data->rc; + PCRE2_SPTR check_subject = start_match; /* start_match includes offset */ + + if (start_offset > 0) + { #if PCRE2_CODE_UNIT_WIDTH != 32 - if (start_offset > 0 && start_offset < length && - NOT_FIRSTCHAR(subject[start_offset])) - return PCRE2_ERROR_BADUTFOFFSET; + unsigned int i; + if (start_match < end_subject && NOT_FIRSTCU(*start_match)) + return PCRE2_ERROR_BADUTFOFFSET; + for (i = re->max_lookbehind; i > 0 && check_subject > subject; i--) + { + check_subject--; + while (check_subject > subject && +#if PCRE2_CODE_UNIT_WIDTH == 8 + (*check_subject & 0xc0) == 0x80) +#else /* 16-bit */ + (*check_subject & 0xfc00) == 0xdc00) +#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */ + check_subject--; + } +#else /* In the 32-bit library, one code unit equals one character. */ + check_subject -= re->max_lookbehind; + if (check_subject < subject) check_subject = subject; #endif /* PCRE2_CODE_UNIT_WIDTH != 32 */ + } + + /* Validate the relevant portion of the subject. After an error, adjust the + offset to be an absolute offset in the whole string. */ + + match_data->rc = PRIV(valid_utf)(check_subject, + length - (check_subject - subject), &(match_data->startchar)); + if (match_data->rc != 0) + { + match_data->startchar += check_subject - subject; + return match_data->rc; + } } #endif /* SUPPORT_UNICODE */ @@ -3507,6 +3544,10 @@ for (;;) /* ------------ End of start of match optimizations ------------ */ + /* Give no match if we have passed the bumpalong limit. */ + + if (start_match > bumpalong_limit) break; + /* OK, now we can do the business */ mb->start_used_ptr = start_match; diff --git a/pcre2-10.20/src/pcre2_error.c b/pcre2-10.21/src/pcre2_error.c similarity index 91% rename from pcre2-10.20/src/pcre2_error.c rename to pcre2-10.21/src/pcre2_error.c index c539bd23e..6b4756aec 100644 --- a/pcre2-10.20/src/pcre2_error.c +++ b/pcre2-10.21/src/pcre2_error.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2015 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -51,11 +51,10 @@ POSSIBILITY OF SUCH DAMAGE. /* The texts of compile-time error messages. Compile-time error numbers start at COMPILE_ERROR_BASE (100). -Do not ever re-use any error number, because they are documented. Always add a -new error instead. This used to be a table of strings, but in order to reduce -the number of relocations needed when a shared library is loaded dynamically, -it is now one long string. We cannot use a table of offsets, because the -lengths of inserts such as XSTRING(MAX_NAME_SIZE) are not known. Instead, +This used to be a table of strings, but in order to reduce the number of +relocations needed when a shared library is loaded dynamically, it is now one +long string. We cannot use a table of offsets, because the lengths of inserts +such as XSTRING(MAX_NAME_SIZE) are not known. Instead, pcre2_get_error_message() counts through to the one it wants - this isn't a performance issue because these strings are used only when there is an error. @@ -92,7 +91,7 @@ static const char compile_error_texts[] = "failed to allocate heap memory\0" "unmatched closing parenthesis\0" "internal error: code overflow\0" - "unrecognized character after (?<\0" + "letter or underscore expected after (?< or (?'\0" /* 25 */ "lookbehind assertion is not fixed length\0" "malformed number or name after (?(\0" @@ -112,7 +111,7 @@ static const char compile_error_texts[] = "number after (?C is greater than 255\0" "closing parenthesis for (?C expected\0" /* 40 */ - "recursion could loop indefinitely\0" + "invalid escape sequence in (*VERB) name\0" "unrecognized character after (?P\0" "syntax error in subpattern name (missing terminator)\0" "two named subpatterns have the same name (PCRE2_DUPNAMES not set)\0" @@ -154,7 +153,7 @@ static const char compile_error_texts[] = /* 70 */ "internal error: unknown opcode in find_fixedlength()\0" "\\N is not supported in a class\0" - "too many forward references\0" + "SPARE ERROR\0" "disallowed Unicode code point (>= 0xd800 && <= 0xdfff)\0" "using UTF is disabled by the application\0" /* 75 */ @@ -169,6 +168,11 @@ static const char compile_error_texts[] = "unrecognized string delimiter follows (?C\0" "using \\C is disabled by the application\0" "(?| and/or (?J: or (?x: parentheses are too deeply nested\0" + /* 85 */ + "using \\C is disabled in this PCRE2 library\0" + "regular expression is too complicated\0" + "lookbehind assertion is too long\0" + "pattern string is longer than the limit set by the application\0" ; /* Match-time and UTF error texts are in the same format. */ @@ -200,7 +204,7 @@ static const char match_error_texts[] = /* 20 */ "UTF-8 error: overlong 5-byte sequence\0" "UTF-8 error: overlong 6-byte sequence\0" - "UTF-8 error: isolated 0x80 byte\0" + "UTF-8 error: isolated byte with 0x80 bit set\0" "UTF-8 error: illegal byte (0xfe or 0xff)\0" "UTF-16 error: missing low surrogate at end\0" /* 25 */ @@ -239,7 +243,15 @@ static const char match_error_texts[] = "nested recursion at the same subject position\0" "recursion limit exceeded\0" "requested value is not available\0" + /* 55 */ "requested value is not set\0" + "offset limit set without PCRE2_USE_OFFSET_LIMIT\0" + "bad escape sequence in replacement string\0" + "expected closing curly bracket in replacement string\0" + "bad substitution in replacement string\0" + /* 60 */ + "match with end before start is not supported\0" + "too many replacements (more than INT_MAX)\0" ; diff --git a/pcre2-10.21/src/pcre2_find_bracket.c b/pcre2-10.21/src/pcre2_find_bracket.c new file mode 100644 index 000000000..803e71976 --- /dev/null +++ b/pcre2-10.21/src/pcre2_find_bracket.c @@ -0,0 +1,218 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Original API code Copyright (c) 1997-2012 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains a single function that scans through a compiled pattern +until it finds a capturing bracket with the given number, or, if the number is +negative, an instance of OP_REVERSE for a lookbehind. The function is called +from pcre2_compile.c and also from pcre2_study.c when finding the minimum +matching length. */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre2_internal.h" + + +/************************************************* +* Scan compiled regex for specific bracket * +*************************************************/ + +/* +Arguments: + code points to start of expression + utf TRUE in UTF mode + number the required bracket number or negative to find a lookbehind + +Returns: pointer to the opcode for the bracket, or NULL if not found +*/ + +PCRE2_SPTR +PRIV(find_bracket)(PCRE2_SPTR code, BOOL utf, int number) +{ +for (;;) + { + register PCRE2_UCHAR c = *code; + + if (c == OP_END) return NULL; + + /* XCLASS is used for classes that cannot be represented just by a bit map. + This includes negated single high-valued characters. CALLOUT_STR is used for + callouts with string arguments. In both cases the length in the table is + zero; the actual length is stored in the compiled code. */ + + if (c == OP_XCLASS) code += GET(code, 1); + else if (c == OP_CALLOUT_STR) code += GET(code, 1 + 2*LINK_SIZE); + + /* Handle lookbehind */ + + else if (c == OP_REVERSE) + { + if (number < 0) return (PCRE2_UCHAR *)code; + code += PRIV(OP_lengths)[c]; + } + + /* Handle capturing bracket */ + + else if (c == OP_CBRA || c == OP_SCBRA || + c == OP_CBRAPOS || c == OP_SCBRAPOS) + { + int n = (int)GET2(code, 1+LINK_SIZE); + if (n == number) return (PCRE2_UCHAR *)code; + code += PRIV(OP_lengths)[c]; + } + + /* Otherwise, we can get the item's length from the table, except that for + repeated character types, we have to test for \p and \P, which have an extra + two bytes of parameters, and for MARK/PRUNE/SKIP/THEN with an argument, we + must add in its length. */ + + else + { + switch(c) + { + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + case OP_TYPEPOSSTAR: + case OP_TYPEPOSPLUS: + case OP_TYPEPOSQUERY: + if (code[1] == OP_PROP || code[1] == OP_NOTPROP) code += 2; + break; + + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + case OP_TYPEEXACT: + case OP_TYPEPOSUPTO: + if (code[1 + IMM2_SIZE] == OP_PROP || code[1 + IMM2_SIZE] == OP_NOTPROP) + code += 2; + break; + + case OP_MARK: + case OP_PRUNE_ARG: + case OP_SKIP_ARG: + case OP_THEN_ARG: + code += code[1]; + break; + } + + /* Add in the fixed length from the table */ + + code += PRIV(OP_lengths)[c]; + + /* In UTF-8 and UTF-16 modes, opcodes that are followed by a character may be + followed by a multi-byte character. The length in the table is a minimum, so + we have to arrange to skip the extra bytes. */ + +#ifdef MAYBE_UTF_MULTI + if (utf) switch(c) + { + case OP_CHAR: + case OP_CHARI: + case OP_NOT: + case OP_NOTI: + case OP_EXACT: + case OP_EXACTI: + case OP_NOTEXACT: + case OP_NOTEXACTI: + case OP_UPTO: + case OP_UPTOI: + case OP_NOTUPTO: + case OP_NOTUPTOI: + case OP_MINUPTO: + case OP_MINUPTOI: + case OP_NOTMINUPTO: + case OP_NOTMINUPTOI: + case OP_POSUPTO: + case OP_POSUPTOI: + case OP_NOTPOSUPTO: + case OP_NOTPOSUPTOI: + case OP_STAR: + case OP_STARI: + case OP_NOTSTAR: + case OP_NOTSTARI: + case OP_MINSTAR: + case OP_MINSTARI: + case OP_NOTMINSTAR: + case OP_NOTMINSTARI: + case OP_POSSTAR: + case OP_POSSTARI: + case OP_NOTPOSSTAR: + case OP_NOTPOSSTARI: + case OP_PLUS: + case OP_PLUSI: + case OP_NOTPLUS: + case OP_NOTPLUSI: + case OP_MINPLUS: + case OP_MINPLUSI: + case OP_NOTMINPLUS: + case OP_NOTMINPLUSI: + case OP_POSPLUS: + case OP_POSPLUSI: + case OP_NOTPOSPLUS: + case OP_NOTPOSPLUSI: + case OP_QUERY: + case OP_QUERYI: + case OP_NOTQUERY: + case OP_NOTQUERYI: + case OP_MINQUERY: + case OP_MINQUERYI: + case OP_NOTMINQUERY: + case OP_NOTMINQUERYI: + case OP_POSQUERY: + case OP_POSQUERYI: + case OP_NOTPOSQUERY: + case OP_NOTPOSQUERYI: + if (HAS_EXTRALEN(code[-1])) code += GET_EXTRALEN(code[-1]); + break; + } +#else + (void)(utf); /* Keep compiler happy by referencing function argument */ +#endif /* MAYBE_UTF_MULTI */ + } + } +} + +/* End of pcre2_find_bracket.c */ diff --git a/pcre2-10.20/src/pcre2_internal.h b/pcre2-10.21/src/pcre2_internal.h similarity index 98% rename from pcre2-10.20/src/pcre2_internal.h rename to pcre2-10.21/src/pcre2_internal.h index a4cf1e08c..7c9f66cc0 100644 --- a/pcre2-10.20/src/pcre2_internal.h +++ b/pcre2-10.21/src/pcre2_internal.h @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2015 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -39,7 +39,10 @@ POSSIBILITY OF SUCH DAMAGE. */ /* We do not support both EBCDIC and Unicode at the same time. The "configure" -script prevents both being selected, but not everybody uses "configure". */ +script prevents both being selected, but not everybody uses "configure". EBCDIC +is only supported for the 8-bit library, but the check for this has to be later +in this file, because the first part is not width-dependent, and is included by +pcre2test.c with CODE_UNIT_WIDTH == 0. */ #if defined EBCDIC && defined SUPPORT_UNICODE #error The use of both EBCDIC and SUPPORT_UNICODE is not supported. @@ -524,9 +527,11 @@ bytes in a code unit in that mode. */ #define PCRE2_NL_SET 0x00008000 /* newline was set in the pattern */ #define PCRE2_NOTEMPTY_SET 0x00010000 /* (*NOTEMPTY) used ) keep */ #define PCRE2_NE_ATST_SET 0x00020000 /* (*NOTEMPTY_ATSTART) used) together */ -#define PCRE2_DEREF_TABLES 0x00040000 /* Release character tables. */ +#define PCRE2_DEREF_TABLES 0x00040000 /* release character tables */ #define PCRE2_NOJIT 0x00080000 /* (*NOJIT) used */ #define PCRE2_HASBKPORX 0x00100000 /* contains \P, \p, or \X */ +#define PCRE2_DUPCAPUSED 0x00200000 /* contains (?| */ +#define PCRE2_HASBKC 0x00400000 /* contains \C */ #define PCRE2_MODE_MASK (PCRE2_MODE8 | PCRE2_MODE16 | PCRE2_MODE32) @@ -917,6 +922,7 @@ a positive value. */ #define STRING_NOTEMPTY_ATSTART_RIGHTPAR "NOTEMPTY_ATSTART)" #define STRING_LIMIT_MATCH_EQ "LIMIT_MATCH=" #define STRING_LIMIT_RECURSION_EQ "LIMIT_RECURSION=" +#define STRING_MARK "MARK" #else /* SUPPORT_UNICODE */ @@ -1189,6 +1195,7 @@ only. */ #define STRING_NOTEMPTY_ATSTART_RIGHTPAR STR_N STR_O STR_T STR_E STR_M STR_P STR_T STR_Y STR_UNDERSCORE STR_A STR_T STR_S STR_T STR_A STR_R STR_T STR_RIGHT_PARENTHESIS #define STRING_LIMIT_MATCH_EQ STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_M STR_A STR_T STR_C STR_H STR_EQUALS_SIGN #define STRING_LIMIT_RECURSION_EQ STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_R STR_E STR_C STR_U STR_R STR_S STR_I STR_O STR_N STR_EQUALS_SIGN +#define STRING_MARK STR_M STR_A STR_R STR_K #endif /* SUPPORT_UNICODE */ @@ -1800,11 +1807,16 @@ typedef struct pcre2_serialized_data { #if defined PCRE2_CODE_UNIT_WIDTH && PCRE2_CODE_UNIT_WIDTH != 0 +/* EBCDIC is supported only for the 8-bit library. */ + +#if defined EBCDIC && PCRE2_CODE_UNIT_WIDTH != 8 +#error EBCDIC is not supported for the 16-bit or 32-bit libraries +#endif + /* This is the largest non-UTF code point. */ #define MAX_NON_UTF_CHAR (0xffffffffU >> (32 - PCRE2_CODE_UNIT_WIDTH)) - /* Internal shared data tables and variables. These are used by more than one of the exported public functions. They have to be "external" in the C sense, but are not part of the PCRE2 public API. Although the data for some of them is @@ -1883,6 +1895,7 @@ not referenced from pcre2test, and must not be defined when no code unit width is available. */ #define _pcre2_auto_possessify PCRE2_SUFFIX(_pcre2_auto_possessify_) +#define _pcre2_check_escape PCRE2_SUFFIX(_pcre2_check_escape_) #define _pcre2_find_bracket PCRE2_SUFFIX(_pcre2_find_bracket_) #define _pcre2_is_newline PCRE2_SUFFIX(_pcre2_is_newline_) #define _pcre2_jit_free_rodata PCRE2_SUFFIX(_pcre2_jit_free_rodata_) @@ -1904,6 +1917,8 @@ is available. */ extern int _pcre2_auto_possessify(PCRE2_UCHAR *, BOOL, const compile_block *); +extern int _pcre2_check_escape(PCRE2_SPTR *, PCRE2_SPTR, uint32_t *, + int *, uint32_t, BOOL, compile_block *); extern PCRE2_SPTR _pcre2_find_bracket(PCRE2_SPTR, BOOL, int); extern BOOL _pcre2_is_newline(PCRE2_SPTR, uint32_t, PCRE2_SPTR, uint32_t *, BOOL); diff --git a/pcre2-10.20/src/pcre2_intmodedep.h b/pcre2-10.21/src/pcre2_intmodedep.h similarity index 96% rename from pcre2-10.20/src/pcre2_intmodedep.h rename to pcre2-10.21/src/pcre2_intmodedep.h index f20f71e1e..90b7959e0 100644 --- a/pcre2-10.20/src/pcre2_intmodedep.h +++ b/pcre2-10.21/src/pcre2_intmodedep.h @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2014 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -72,7 +72,7 @@ just to undefine them all. */ #undef MAX_MARK #undef MAX_PATTERN_SIZE #undef MAX_UTF_SINGLE_CU -#undef NOT_FIRSTCHAR +#undef NOT_FIRSTCU #undef PUT #undef PUT2 #undef PUT2INC @@ -252,7 +252,7 @@ UTF support is omitted, we don't even define them. */ /* #define MAX_UTF_SINGLE_CU */ /* #define HAS_EXTRALEN(c) */ /* #define GET_EXTRALEN(c) */ -/* #define NOT_FIRSTCHAR(c) */ +/* #define NOT_FIRSTCU(c) */ #define GETCHAR(c, eptr) c = *eptr; #define GETCHARTEST(c, eptr) c = *eptr; #define GETCHARINC(c, eptr) c = *eptr++; @@ -285,10 +285,10 @@ Otherwise it has an undefined behaviour. */ #define GET_EXTRALEN(c) (PRIV(utf8_table4)[(c) & 0x3f]) -/* Returns TRUE, if the given character is not the first character -of a UTF sequence. */ +/* Returns TRUE, if the given value is not the first code unit of a UTF +sequence. */ -#define NOT_FIRSTCHAR(c) (((c) & 0xc0) == 0x80) +#define NOT_FIRSTCU(c) (((c) & 0xc0) == 0x80) /* Get the next UTF-8 character, not advancing the pointer. This is called when we know we are in UTF-8 mode. */ @@ -371,10 +371,10 @@ Otherwise it has an undefined behaviour. */ #define GET_EXTRALEN(c) 1 -/* Returns TRUE, if the given character is not the first character -of a UTF sequence. */ +/* Returns TRUE, if the given value is not the first code unit of a UTF +sequence. */ -#define NOT_FIRSTCHAR(c) (((c) & 0xfc00) == 0xdc00) +#define NOT_FIRSTCU(c) (((c) & 0xfc00) == 0xdc00) /* Base macro to pick up the low surrogate of a UTF-16 character, not advancing the pointer. */ @@ -469,7 +469,7 @@ into one PCRE2_UCHAR unit. */ #define MAX_UTF_SINGLE_CU (0x10ffffu) #define HAS_EXTRALEN(c) (0) #define GET_EXTRALEN(c) (0) -#define NOT_FIRSTCHAR(c) (0) +#define NOT_FIRSTCU(c) (0) /* Get the next UTF-32 character, not advancing the pointer. This is called when we know we are in UTF-32 mode. */ @@ -562,6 +562,7 @@ typedef struct pcre2_real_compile_context { int (*stack_guard)(uint32_t, void *); void *stack_guard_data; const uint8_t *tables; + PCRE2_SIZE max_pattern_length; uint16_t bsr_convention; uint16_t newline_convention; uint32_t parens_nest_limit; @@ -580,6 +581,7 @@ typedef struct pcre2_real_match_context { #endif int (*callout)(pcre2_callout_block *, void *); void *callout_data; + PCRE2_SIZE offset_limit; uint32_t match_limit; uint32_t recursion_limit; } pcre2_real_match_context; @@ -588,11 +590,17 @@ typedef struct pcre2_real_match_context { defined specially because it is required in pcre2_serialize_decode() when copying the size from possibly unaligned memory into a variable of the same type. Use a macro rather than a typedef to avoid compiler warnings when this -file is included multiple times by pcre2test. */ +file is included multiple times by pcre2test. LOOKBEHIND_MAX specifies the +largest lookbehind that is supported. (OP_REVERSE in a pattern has a 16-bit +argument in 8-bit and 16-bit modes, so we need no more than a 16-bit field +here.) */ #undef CODE_BLOCKSIZE_TYPE #define CODE_BLOCKSIZE_TYPE size_t +#undef LOOKBEHIND_MAX +#define LOOKBEHIND_MAX UINT16_MAX + typedef struct pcre2_real_code { pcre2_memctl memctl; /* Memory control fields */ const uint8_t *tables; /* The character tables */ @@ -647,6 +655,13 @@ typedef struct recurse_check { PCRE2_SPTR group; } recurse_check; +/* Structure for building a cache when filling in recursion offsets. */ + +typedef struct recurse_cache { + PCRE2_SPTR group; + int recno; +} recurse_cache; + /* Structure for maintaining a chain of pointers to the currently incomplete branches, for testing for left recursion while compiling. */ @@ -678,7 +693,7 @@ typedef struct compile_block { PCRE2_SPTR start_code; /* The start of the compiled code */ PCRE2_SPTR start_pattern; /* The start of the pattern */ PCRE2_SPTR end_pattern; /* The end of the pattern */ - PCRE2_UCHAR *hwm; /* High watermark of workspace */ + PCRE2_SPTR nestptr[2]; /* Pointer(s) saved for string substitution */ PCRE2_UCHAR *name_table; /* The name/number table */ size_t workspace_size; /* Size of workspace */ uint16_t names_found; /* Number of entries so far */ @@ -690,6 +705,7 @@ typedef struct compile_block { uint32_t external_flags; /* External flag bits to be set */ uint32_t bracount; /* Count of capturing parens as we compile */ uint32_t final_bracount; /* Saved value after first pass */ + uint32_t *groupinfo; /* Group info vector */ uint32_t top_backref; /* Maximum back reference */ uint32_t backref_map; /* Bitmap of low back refs */ uint32_t nltype; /* Newline type */ @@ -701,6 +717,7 @@ typedef struct compile_block { int req_varyopt; /* "After variable item" flag for reqbyte */ BOOL had_accept; /* (*ACCEPT) encountered */ BOOL had_pruneorskip; /* (*PRUNE) or (*SKIP) encountered */ + BOOL had_recurse; /* Had a recursion or subroutine call */ BOOL check_lookbehind; /* Lookbehinds need later checking */ BOOL dupnames; /* Duplicate names exist */ BOOL iscondassert; /* Next assert is a condition */ diff --git a/pcre2-10.20/src/pcre2_jit_compile.c b/pcre2-10.21/src/pcre2_jit_compile.c similarity index 91% rename from pcre2-10.20/src/pcre2_jit_compile.c rename to pcre2-10.21/src/pcre2_jit_compile.c index 272ab2857..b46f4e394 100644 --- a/pcre2-10.20/src/pcre2_jit_compile.c +++ b/pcre2-10.21/src/pcre2_jit_compile.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2014 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -186,6 +186,7 @@ typedef struct jit_arguments { int (*callout)(pcre2_callout_block *, void *); void *callout_data; /* Everything else after. */ + sljit_uw offset_limit; sljit_ui limit_match; uint32_t oveccount; uint32_t options; @@ -359,33 +360,38 @@ typedef struct compiler_common { /* Current position where a THEN must jump. */ then_trap_backtrack *then_trap; /* Starting offset of private data for capturing brackets. */ - int cbra_ptr; + sljit_si cbra_ptr; /* Output vector starting point. Must be divisible by 2. */ - int ovector_start; + sljit_si ovector_start; /* Points to the starting character of the current match. */ - int start_ptr; + sljit_si start_ptr; /* Last known position of the requested byte. */ - int req_char_ptr; + sljit_si req_char_ptr; /* Head of the last recursion. */ - int recursive_head_ptr; + sljit_si recursive_head_ptr; /* First inspected character for partial matching. (Needed for avoiding zero length partial matches.) */ - int start_used_ptr; + sljit_si start_used_ptr; /* Starting pointer for partial soft matches. */ - int hit_start; - /* End pointer of the first line. */ - int first_line_end; + sljit_si hit_start; + /* Pointer of the match end position. */ + sljit_si match_end_ptr; /* Points to the marked string. */ - int mark_ptr; + sljit_si mark_ptr; /* Recursive control verb management chain. */ - int control_head_ptr; + sljit_si control_head_ptr; /* Points to the last matched capture block index. */ - int capture_last_ptr; + sljit_si capture_last_ptr; + /* Fast forward skipping byte code pointer. */ + PCRE2_SPTR fast_forward_bc_ptr; + /* Locals used by fast fail optimization. */ + sljit_si fast_fail_start_ptr; + sljit_si fast_fail_end_ptr; /* Flipped and lower case tables. */ const sljit_ub *fcc; sljit_sw lcc; - /* Mode can be PCRE_STUDY_JIT_COMPILE and others. */ + /* Mode can be PCRE2_JIT_COMPLETE and others. */ int mode; /* TRUE, when minlength is greater than 0. */ BOOL might_be_empty; @@ -395,6 +401,8 @@ typedef struct compiler_common { BOOL has_skip_arg; /* (*THEN) is found in the pattern. */ BOOL has_then; + /* (*SKIP) or (*SKIP:arg) is found in lookbehind assertion. */ + BOOL has_skip_in_assert_back; /* Currently in recurse or negative assert. */ BOOL local_exit; /* Currently in a positive assert. */ @@ -593,11 +601,6 @@ SLJIT_ASSERT(*cc >= OP_KET && *cc <= OP_KETRPOS); return count; } -static int ones_in_half_byte[16] = { - /* 0 */ 0, 1, 1, 2, /* 4 */ 1, 2, 2, 3, - /* 8 */ 1, 2, 2, 3, /* 12 */ 2, 3, 3, 4 -}; - /* Functions whose might need modification for all new supported opcodes: next_opcode check_opcode_types @@ -813,6 +816,7 @@ static BOOL check_opcode_types(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPT { int count; PCRE2_SPTR slot; +PCRE2_SPTR assert_back_end = cc - 1; /* Calculate important variables (like stack size) and checks whether all opcodes are supported. */ while (cc < ccend) @@ -884,6 +888,13 @@ while (cc < ccend) cc += (*cc == OP_CALLOUT) ? PRIV(OP_lengths)[OP_CALLOUT] : GET(cc, 1 + 2*LINK_SIZE); break; + case OP_ASSERTBACK: + slot = bracketend(cc); + if (slot > assert_back_end) + assert_back_end = slot; + cc += 1 + LINK_SIZE; + break; + case OP_THEN_ARG: common->has_then = TRUE; common->control_head_ptr = 1; @@ -905,9 +916,17 @@ while (cc < ccend) cc += 1; break; + case OP_SKIP: + if (cc < assert_back_end) + common->has_skip_in_assert_back = TRUE; + cc += 1; + break; + case OP_SKIP_ARG: common->control_head_ptr = 1; common->has_skip_arg = TRUE; + if (cc < assert_back_end) + common->has_skip_in_assert_back = TRUE; cc += 1 + 2 + cc[1]; break; @@ -921,6 +940,185 @@ while (cc < ccend) return TRUE; } +static BOOL is_accelerated_repeat(PCRE2_SPTR cc) +{ +switch(*cc) + { + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEPOSSTAR: + case OP_TYPEPOSPLUS: + return (cc[1] != OP_ANYNL && cc[1] != OP_EXTUNI); + + case OP_STAR: + case OP_MINSTAR: + case OP_PLUS: + case OP_MINPLUS: + case OP_POSSTAR: + case OP_POSPLUS: + + case OP_STARI: + case OP_MINSTARI: + case OP_PLUSI: + case OP_MINPLUSI: + case OP_POSSTARI: + case OP_POSPLUSI: + + case OP_NOTSTAR: + case OP_NOTMINSTAR: + case OP_NOTPLUS: + case OP_NOTMINPLUS: + case OP_NOTPOSSTAR: + case OP_NOTPOSPLUS: + + case OP_NOTSTARI: + case OP_NOTMINSTARI: + case OP_NOTPLUSI: + case OP_NOTMINPLUSI: + case OP_NOTPOSSTARI: + case OP_NOTPOSPLUSI: + return TRUE; + + case OP_CLASS: + case OP_NCLASS: +#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8 + case OP_XCLASS: + cc += (*cc == OP_XCLASS) ? GET(cc, 1) : (int)(1 + (32 / sizeof(PCRE2_UCHAR))); +#else + cc += (1 + (32 / sizeof(PCRE2_UCHAR))); +#endif + + switch(*cc) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRPOSSTAR: + case OP_CRPOSPLUS: + return TRUE; + } + break; + } +return FALSE; +} + +static SLJIT_INLINE BOOL detect_fast_forward_skip(compiler_common *common, int *private_data_start) +{ +PCRE2_SPTR cc = common->start; +PCRE2_SPTR end; + +/* Skip not repeated brackets. */ +while (TRUE) + { + switch(*cc) + { + case OP_SOD: + case OP_SOM: + case OP_SET_SOM: + case OP_NOT_WORD_BOUNDARY: + case OP_WORD_BOUNDARY: + case OP_EODN: + case OP_EOD: + case OP_CIRC: + case OP_CIRCM: + case OP_DOLL: + case OP_DOLLM: + /* Zero width assertions. */ + cc++; + continue; + } + + if (*cc != OP_BRA && *cc != OP_CBRA) + break; + + end = cc + GET(cc, 1); + if (*end != OP_KET || PRIVATE_DATA(end) != 0) + return FALSE; + if (*cc == OP_CBRA) + { + if (common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] == 0) + return FALSE; + cc += IMM2_SIZE; + } + cc += 1 + LINK_SIZE; + } + +if (is_accelerated_repeat(cc)) + { + common->fast_forward_bc_ptr = cc; + common->private_data_ptrs[(cc + 1) - common->start] = *private_data_start; + *private_data_start += sizeof(sljit_sw); + return TRUE; + } +return FALSE; +} + +static SLJIT_INLINE void detect_fast_fail(compiler_common *common, PCRE2_SPTR cc, int *private_data_start, sljit_si depth) +{ + PCRE2_SPTR next_alt; + + SLJIT_ASSERT(*cc == OP_BRA || *cc == OP_CBRA); + + if (*cc == OP_CBRA && common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] == 0) + return; + + next_alt = bracketend(cc) - (1 + LINK_SIZE); + if (*next_alt != OP_KET || PRIVATE_DATA(next_alt) != 0) + return; + + do + { + next_alt = cc + GET(cc, 1); + + cc += 1 + LINK_SIZE + ((*cc == OP_CBRA) ? IMM2_SIZE : 0); + + while (TRUE) + { + switch(*cc) + { + case OP_SOD: + case OP_SOM: + case OP_SET_SOM: + case OP_NOT_WORD_BOUNDARY: + case OP_WORD_BOUNDARY: + case OP_EODN: + case OP_EOD: + case OP_CIRC: + case OP_CIRCM: + case OP_DOLL: + case OP_DOLLM: + /* Zero width assertions. */ + cc++; + continue; + } + break; + } + + if (depth > 0 && (*cc == OP_BRA || *cc == OP_CBRA)) + detect_fast_fail(common, cc, private_data_start, depth - 1); + + if (is_accelerated_repeat(cc)) + { + common->private_data_ptrs[(cc + 1) - common->start] = *private_data_start; + + if (common->fast_fail_start_ptr == 0) + common->fast_fail_start_ptr = *private_data_start; + + *private_data_start += sizeof(sljit_sw); + common->fast_fail_end_ptr = *private_data_start; + + if (*private_data_start > SLJIT_MAX_LOCAL_SIZE) + return; + } + + cc = next_alt; + } + while (*cc == OP_ALT); +} + static int get_class_iterator_size(PCRE2_SPTR cc) { sljit_ui min; @@ -1097,6 +1295,7 @@ PCRE2_SPTR alternative; PCRE2_SPTR end = NULL; int private_data_ptr = *private_data_start; int space, size, bracketlen; +BOOL repeat_check = TRUE; while (cc < ccend) { @@ -1106,7 +1305,8 @@ while (cc < ccend) if (private_data_ptr > SLJIT_MAX_LOCAL_SIZE) break; - if (*cc == OP_ONCE || *cc == OP_ONCE_NC || *cc == OP_BRA || *cc == OP_CBRA || *cc == OP_COND) + if (repeat_check && (*cc == OP_ONCE || *cc == OP_ONCE_NC || *cc == OP_BRA || *cc == OP_CBRA || *cc == OP_COND)) + { if (detect_repeat(common, cc)) { /* These brackets are converted to repeats, so no global @@ -1114,6 +1314,8 @@ while (cc < ccend) if (cc >= end) end = bracketend(cc); } + } + repeat_check = TRUE; switch(*cc) { @@ -1169,6 +1371,13 @@ while (cc < ccend) bracketlen = 1 + LINK_SIZE + IMM2_SIZE; break; + case OP_BRAZERO: + case OP_BRAMINZERO: + case OP_BRAPOSZERO: + repeat_check = FALSE; + size = 1; + break; + CASE_ITERATOR_PRIVATE_DATA_1 space = 1; size = -2; @@ -1354,6 +1563,13 @@ while (cc < ccend) cc += 1 + LINK_SIZE + IMM2_SIZE; break; + case OP_THEN: + stack_restore = TRUE; + if (common->control_head_ptr != 0) + *needs_control_head = TRUE; + cc ++; + break; + default: stack_restore = TRUE; /* Fall through. */ @@ -2174,7 +2390,7 @@ static SLJIT_INLINE void reset_ovector(compiler_common *common, int length) { DEFINE_COMPILER; struct sljit_label *loop; -int i; +sljit_si i; /* At this point we can freely use all temporary registers. */ SLJIT_ASSERT(length > 1); @@ -2196,6 +2412,18 @@ else } } +static SLJIT_INLINE void reset_fast_fail(compiler_common *common) +{ +DEFINE_COMPILER; +sljit_si i; + +SLJIT_ASSERT(common->fast_fail_start_ptr < common->fast_fail_end_ptr); + +OP2(SLJIT_SUB, TMP1, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +for (i = common->fast_fail_start_ptr; i < common->fast_fail_end_ptr; i += sizeof(sljit_sw)) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), i, TMP1, 0); +} + static SLJIT_INLINE void do_reset_match(compiler_common *common, int length) { DEFINE_COMPILER; @@ -2249,6 +2477,7 @@ while (current != NULL) SLJIT_ASSERT_STOP(); break; } + SLJIT_ASSERT(current > (sljit_sw*)current[-1]); current = (sljit_sw*)current[-1]; } return -1; @@ -3070,14 +3299,14 @@ sljit_emit_fast_return(compiler, RETURN_ADDR, 0); #endif /* SUPPORT_UNICODE */ -static SLJIT_INLINE struct sljit_label *mainloop_entry(compiler_common *common, BOOL hascrorlf, BOOL firstline) +static SLJIT_INLINE struct sljit_label *mainloop_entry(compiler_common *common, BOOL hascrorlf, uint32_t overall_options) { DEFINE_COMPILER; struct sljit_label *mainloop; struct sljit_label *newlinelabel = NULL; struct sljit_jump *start; struct sljit_jump *end = NULL; -struct sljit_jump *nl = NULL; +struct sljit_jump *end2 = NULL; #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 struct sljit_jump *singlechar; #endif @@ -3085,14 +3314,16 @@ jump_list *newline = NULL; BOOL newlinecheck = FALSE; BOOL readuchar = FALSE; -if (!(hascrorlf || firstline) && (common->nltype == NLTYPE_ANY || - common->nltype == NLTYPE_ANYCRLF || common->newline > 255)) +if (!(hascrorlf || (overall_options & PCRE2_FIRSTLINE) != 0) + && (common->nltype == NLTYPE_ANY || common->nltype == NLTYPE_ANYCRLF || common->newline > 255)) newlinecheck = TRUE; -if (firstline) +SLJIT_ASSERT(common->forced_quit_label == NULL); + +if ((overall_options & PCRE2_FIRSTLINE) != 0) { /* Search for the end of the first line. */ - SLJIT_ASSERT(common->first_line_end != 0); + SLJIT_ASSERT(common->match_end_ptr != 0); OP1(SLJIT_MOV, TMP3, 0, STR_PTR, 0); if (common->nltype == NLTYPE_FIXED && common->newline > 255) @@ -3105,24 +3336,49 @@ if (firstline) CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff, mainloop); CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff, mainloop); JUMPHERE(end); - OP2(SLJIT_SUB, SLJIT_MEM1(SLJIT_SP), common->first_line_end, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + OP2(SLJIT_SUB, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); } else { end = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); mainloop = LABEL(); /* Continual stores does not cause data dependency. */ - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->first_line_end, STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr, STR_PTR, 0); read_char_range(common, common->nlmin, common->nlmax, TRUE); check_newlinechar(common, common->nltype, &newline, TRUE); CMPTO(SLJIT_LESS, STR_PTR, 0, STR_END, 0, mainloop); JUMPHERE(end); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->first_line_end, STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr, STR_PTR, 0); set_jumps(newline, LABEL()); } OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0); } +else if ((overall_options & PCRE2_USE_OFFSET_LIMIT) != 0) + { + /* Check whether offset limit is set and valid. */ + SLJIT_ASSERT(common->match_end_ptr != 0); + + OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, offset_limit)); + OP1(SLJIT_MOV, TMP2, 0, STR_END, 0); + end = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, (sljit_sw) PCRE2_UNSET); + OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0); +#if PCRE2_CODE_UNIT_WIDTH == 16 + OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); +#elif PCRE2_CODE_UNIT_WIDTH == 32 + OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 2); +#endif + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, begin)); + OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0); + end2 = CMP(SLJIT_LESS_EQUAL, TMP2, 0, STR_END, 0); + OP1(SLJIT_MOV, TMP2, 0, STR_END, 0); + JUMPHERE(end2); + OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_NOMATCH); + add_jump(compiler, &common->forced_quit, CMP(SLJIT_LESS, TMP2, 0, STR_PTR, 0)); + JUMPHERE(end); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr, TMP2, 0); + } start = JUMP(SLJIT_JUMP); @@ -3138,7 +3394,7 @@ if (newlinecheck) OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT); #endif OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); - nl = JUMP(SLJIT_JUMP); + end2 = JUMP(SLJIT_JUMP); } mainloop = LABEL(); @@ -3183,51 +3439,52 @@ JUMPHERE(start); if (newlinecheck) { JUMPHERE(end); - JUMPHERE(nl); + JUMPHERE(end2); } return mainloop; } #define MAX_N_CHARS 16 -#define MAX_N_BYTES 8 +#define MAX_DIFF_CHARS 6 -static SLJIT_INLINE void add_prefix_byte(sljit_ub byte, sljit_ub *bytes) +static SLJIT_INLINE void add_prefix_char(PCRE2_UCHAR chr, PCRE2_UCHAR *chars) { -sljit_ub len = bytes[0]; -int i; +PCRE2_UCHAR i, len; +len = chars[0]; if (len == 255) return; if (len == 0) { - bytes[0] = 1; - bytes[1] = byte; + chars[0] = 1; + chars[1] = chr; return; } for (i = len; i > 0; i--) - if (bytes[i] == byte) + if (chars[i] == chr) return; -if (len >= MAX_N_BYTES - 1) +if (len >= MAX_DIFF_CHARS - 1) { - bytes[0] = 255; + chars[0] = 255; return; } len++; -bytes[len] = byte; -bytes[0] = len; +chars[len] = chr; +chars[0] = len; } -static int scan_prefix(compiler_common *common, PCRE2_SPTR cc, sljit_ui *chars, sljit_ub *bytes, int max_chars) +static int scan_prefix(compiler_common *common, PCRE2_SPTR cc, PCRE2_UCHAR *chars, int max_chars, uint32_t *rec_count) { /* Recursive function, which scans prefix literals. */ -BOOL last, any, caseless; +BOOL last, any, class, caseless; int len, repeat, len_save, consumed = 0; -sljit_ui chr, mask; +sljit_ui chr; /* Any unicode character. */ +sljit_ub *bytes, *bytes_end, byte; PCRE2_SPTR alternative, cc_save, oc; #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 PCRE2_UCHAR othercase[8]; @@ -3240,9 +3497,15 @@ PCRE2_UCHAR othercase[1]; repeat = 1; while (TRUE) { + if (*rec_count == 0) + return 0; + (*rec_count)--; + last = TRUE; any = FALSE; + class = FALSE; caseless = FALSE; + switch (*cc) { case OP_CHARI: @@ -3304,7 +3567,7 @@ while (TRUE) #ifdef SUPPORT_UNICODE if (common->utf && HAS_EXTRALEN(*cc)) len += GET_EXTRALEN(*cc); #endif - max_chars = scan_prefix(common, cc + len, chars, bytes, max_chars); + max_chars = scan_prefix(common, cc + len, chars, max_chars, rec_count); if (max_chars == 0) return consumed; last = FALSE; @@ -3327,7 +3590,7 @@ while (TRUE) alternative = cc + GET(cc, 1); while (*alternative == OP_ALT) { - max_chars = scan_prefix(common, alternative + 1 + LINK_SIZE, chars, bytes, max_chars); + max_chars = scan_prefix(common, alternative + 1 + LINK_SIZE, chars, max_chars, rec_count); if (max_chars == 0) return consumed; alternative += GET(alternative, 1); @@ -3342,16 +3605,14 @@ while (TRUE) #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 if (common->utf && !is_char7_bitset((const sljit_ub *)(cc + 1), FALSE)) return consumed; #endif - any = TRUE; - cc += 1 + 32 / sizeof(PCRE2_UCHAR); + class = TRUE; break; case OP_NCLASS: #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 if (common->utf) return consumed; #endif - any = TRUE; - cc += 1 + 32 / sizeof(PCRE2_UCHAR); + class = TRUE; break; #if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8 @@ -3439,27 +3700,14 @@ while (TRUE) if (any) { -#if PCRE2_CODE_UNIT_WIDTH == 8 - mask = 0xff; -#elif PCRE2_CODE_UNIT_WIDTH == 16 - mask = 0xffff; -#elif PCRE2_CODE_UNIT_WIDTH == 32 - mask = 0xffffffff; -#else - SLJIT_ASSERT_STOP(); -#endif - do { - chars[0] = mask; - chars[1] = mask; - bytes[0] = 255; + chars[0] = 255; consumed++; if (--max_chars == 0) return consumed; - chars += 2; - bytes += MAX_N_BYTES; + chars += MAX_DIFF_CHARS; } while (--repeat > 0); @@ -3467,6 +3715,103 @@ while (TRUE) continue; } + if (class) + { + bytes = (sljit_ub*) (cc + 1); + cc += 1 + 32 / sizeof(PCRE2_UCHAR); + + switch (*cc) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPOSSTAR: + case OP_CRQUERY: + case OP_CRMINQUERY: + case OP_CRPOSQUERY: + max_chars = scan_prefix(common, cc + 1, chars, max_chars, rec_count); + if (max_chars == 0) + return consumed; + break; + + default: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRPOSPLUS: + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + case OP_CRPOSRANGE: + repeat = GET2(cc, 1); + if (repeat <= 0) + return consumed; + break; + } + + do + { + if (bytes[31] & 0x80) + chars[0] = 255; + else if (chars[0] != 255) + { + bytes_end = bytes + 32; + chr = 0; + do + { + byte = *bytes++; + SLJIT_ASSERT((chr & 0x7) == 0); + if (byte == 0) + chr += 8; + else + { + do + { + if ((byte & 0x1) != 0) + add_prefix_char(chr, chars); + byte >>= 1; + chr++; + } + while (byte != 0); + chr = (chr + 7) & ~7; + } + } + while (chars[0] != 255 && bytes < bytes_end); + bytes = bytes_end - 32; + } + + consumed++; + if (--max_chars == 0) + return consumed; + chars += MAX_DIFF_CHARS; + } + while (--repeat > 0); + + switch (*cc) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPOSSTAR: + return consumed; + + case OP_CRQUERY: + case OP_CRMINQUERY: + case OP_CRPOSQUERY: + cc++; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + case OP_CRPOSRANGE: + if (GET2(cc, 1) != GET2(cc, 1 + IMM2_SIZE)) + return consumed; + cc += 1 + 2 * IMM2_SIZE; + break; + } + + repeat = 1; + continue; + } + len = 1; #ifdef SUPPORT_UNICODE if (common->utf && HAS_EXTRALEN(*cc)) len += GET_EXTRALEN(*cc); @@ -3502,43 +3847,16 @@ while (TRUE) do { chr = *cc; -#if PCRE2_CODE_UNIT_WIDTH == 32 - if (SLJIT_UNLIKELY(chr == NOTACHAR)) - return consumed; -#endif - add_prefix_byte((sljit_ub)chr, bytes); + add_prefix_char(*cc, chars); - mask = 0; if (caseless) - { - add_prefix_byte((sljit_ub)*oc, bytes); - mask = *cc ^ *oc; - chr |= mask; - } - -#if PCRE2_CODE_UNIT_WIDTH == 32 - if (chars[0] == NOTACHAR && chars[1] == 0) -#else - if (chars[0] == NOTACHAR) -#endif - { - chars[0] = chr; - chars[1] = mask; - } - else - { - mask |= chars[0] ^ chr; - chr |= mask; - chars[0] = chr; - chars[1] |= mask; - } + add_prefix_char(*oc, chars); len--; consumed++; if (--max_chars == 0) return consumed; - chars += 2; - bytes += MAX_N_BYTES; + chars += MAX_DIFF_CHARS; cc++; oc++; } @@ -3557,65 +3875,504 @@ while (TRUE) } } -static SLJIT_INLINE BOOL fast_forward_first_n_chars(compiler_common *common, BOOL firstline) +#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) + +static sljit_si character_to_int32(PCRE2_UCHAR chr) +{ +sljit_si value = (sljit_si)chr; +#if PCRE2_CODE_UNIT_WIDTH == 8 +#define SSE2_COMPARE_TYPE_INDEX 0 +return (value << 24) | (value << 16) | (value << 8) | value; +#elif PCRE2_CODE_UNIT_WIDTH == 16 +#define SSE2_COMPARE_TYPE_INDEX 1 +return (value << 16) | value; +#elif PCRE2_CODE_UNIT_WIDTH == 32 +#define SSE2_COMPARE_TYPE_INDEX 2 +return value; +#else +#error "Unsupported unit width" +#endif +} + +static SLJIT_INLINE void fast_forward_first_char2_sse2(compiler_common *common, PCRE2_UCHAR char1, PCRE2_UCHAR char2) +{ +DEFINE_COMPILER; +struct sljit_label *start; +struct sljit_jump *quit[3]; +struct sljit_jump *nomatch; +sljit_ub instruction[8]; +sljit_si tmp1_ind = sljit_get_register_index(TMP1); +sljit_si tmp2_ind = sljit_get_register_index(TMP2); +sljit_si str_ptr_ind = sljit_get_register_index(STR_PTR); +BOOL load_twice = FALSE; +PCRE2_UCHAR bit; + +bit = char1 ^ char2; +if (!is_powerof2(bit)) + bit = 0; + +if ((char1 != char2) && bit == 0) + load_twice = TRUE; + +quit[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); + +/* First part (unaligned start) */ + +OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char1 | bit)); + +SLJIT_ASSERT(tmp1_ind < 8 && tmp2_ind == 1); + +/* MOVD xmm, r/m32 */ +instruction[0] = 0x66; +instruction[1] = 0x0f; +instruction[2] = 0x6e; +instruction[3] = 0xc0 | (2 << 3) | tmp1_ind; +sljit_emit_op_custom(compiler, instruction, 4); + +if (char1 != char2) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(bit != 0 ? bit : char2)); + + /* MOVD xmm, r/m32 */ + instruction[3] = 0xc0 | (3 << 3) | tmp1_ind; + sljit_emit_op_custom(compiler, instruction, 4); + } + +/* PSHUFD xmm1, xmm2/m128, imm8 */ +instruction[2] = 0x70; +instruction[3] = 0xc0 | (2 << 3) | 2; +instruction[4] = 0; +sljit_emit_op_custom(compiler, instruction, 5); + +if (char1 != char2) + { + /* PSHUFD xmm1, xmm2/m128, imm8 */ + instruction[3] = 0xc0 | (3 << 3) | 3; + instruction[4] = 0; + sljit_emit_op_custom(compiler, instruction, 5); + } + +OP2(SLJIT_AND, TMP2, 0, STR_PTR, 0, SLJIT_IMM, 0xf); +OP2(SLJIT_AND, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, ~0xf); + +/* MOVDQA xmm1, xmm2/m128 */ +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + +if (str_ptr_ind < 8) + { + instruction[2] = 0x6f; + instruction[3] = (0 << 3) | str_ptr_ind; + sljit_emit_op_custom(compiler, instruction, 4); + + if (load_twice) + { + instruction[3] = (1 << 3) | str_ptr_ind; + sljit_emit_op_custom(compiler, instruction, 4); + } + } +else + { + instruction[1] = 0x41; + instruction[2] = 0x0f; + instruction[3] = 0x6f; + instruction[4] = (0 << 3) | (str_ptr_ind & 0x7); + sljit_emit_op_custom(compiler, instruction, 5); + + if (load_twice) + { + instruction[4] = (1 << 3) | str_ptr_ind; + sljit_emit_op_custom(compiler, instruction, 5); + } + instruction[1] = 0x0f; + } + +#else + +instruction[2] = 0x6f; +instruction[3] = (0 << 3) | str_ptr_ind; +sljit_emit_op_custom(compiler, instruction, 4); + +if (load_twice) + { + instruction[3] = (1 << 3) | str_ptr_ind; + sljit_emit_op_custom(compiler, instruction, 4); + } + +#endif + +if (bit != 0) + { + /* POR xmm1, xmm2/m128 */ + instruction[2] = 0xeb; + instruction[3] = 0xc0 | (0 << 3) | 3; + sljit_emit_op_custom(compiler, instruction, 4); + } + +/* PCMPEQB/W/D xmm1, xmm2/m128 */ +instruction[2] = 0x74 + SSE2_COMPARE_TYPE_INDEX; +instruction[3] = 0xc0 | (0 << 3) | 2; +sljit_emit_op_custom(compiler, instruction, 4); + +if (load_twice) + { + instruction[3] = 0xc0 | (1 << 3) | 3; + sljit_emit_op_custom(compiler, instruction, 4); + } + +/* PMOVMSKB reg, xmm */ +instruction[2] = 0xd7; +instruction[3] = 0xc0 | (tmp1_ind << 3) | 0; +sljit_emit_op_custom(compiler, instruction, 4); + +if (load_twice) + { + OP1(SLJIT_MOV, TMP3, 0, TMP2, 0); + instruction[3] = 0xc0 | (tmp2_ind << 3) | 1; + sljit_emit_op_custom(compiler, instruction, 4); + + OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); + OP1(SLJIT_MOV, TMP2, 0, TMP3, 0); + } + +OP2(SLJIT_ASHR, TMP1, 0, TMP1, 0, TMP2, 0); + +/* BSF r32, r/m32 */ +instruction[0] = 0x0f; +instruction[1] = 0xbc; +instruction[2] = 0xc0 | (tmp1_ind << 3) | tmp1_ind; +sljit_emit_op_custom(compiler, instruction, 3); + +nomatch = JUMP(SLJIT_ZERO); + +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0); +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); +quit[1] = JUMP(SLJIT_JUMP); + +JUMPHERE(nomatch); + +start = LABEL(); +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, 16); +quit[2] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); + +/* Second part (aligned) */ + +instruction[0] = 0x66; +instruction[1] = 0x0f; + +/* MOVDQA xmm1, xmm2/m128 */ +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + +if (str_ptr_ind < 8) + { + instruction[2] = 0x6f; + instruction[3] = (0 << 3) | str_ptr_ind; + sljit_emit_op_custom(compiler, instruction, 4); + + if (load_twice) + { + instruction[3] = (1 << 3) | str_ptr_ind; + sljit_emit_op_custom(compiler, instruction, 4); + } + } +else + { + instruction[1] = 0x41; + instruction[2] = 0x0f; + instruction[3] = 0x6f; + instruction[4] = (0 << 3) | (str_ptr_ind & 0x7); + sljit_emit_op_custom(compiler, instruction, 5); + + if (load_twice) + { + instruction[4] = (1 << 3) | str_ptr_ind; + sljit_emit_op_custom(compiler, instruction, 5); + } + instruction[1] = 0x0f; + } + +#else + +instruction[2] = 0x6f; +instruction[3] = (0 << 3) | str_ptr_ind; +sljit_emit_op_custom(compiler, instruction, 4); + +if (load_twice) + { + instruction[3] = (1 << 3) | str_ptr_ind; + sljit_emit_op_custom(compiler, instruction, 4); + } + +#endif + +if (bit != 0) + { + /* POR xmm1, xmm2/m128 */ + instruction[2] = 0xeb; + instruction[3] = 0xc0 | (0 << 3) | 3; + sljit_emit_op_custom(compiler, instruction, 4); + } + +/* PCMPEQB/W/D xmm1, xmm2/m128 */ +instruction[2] = 0x74 + SSE2_COMPARE_TYPE_INDEX; +instruction[3] = 0xc0 | (0 << 3) | 2; +sljit_emit_op_custom(compiler, instruction, 4); + +if (load_twice) + { + instruction[3] = 0xc0 | (1 << 3) | 3; + sljit_emit_op_custom(compiler, instruction, 4); + } + +/* PMOVMSKB reg, xmm */ +instruction[2] = 0xd7; +instruction[3] = 0xc0 | (tmp1_ind << 3) | 0; +sljit_emit_op_custom(compiler, instruction, 4); + +if (load_twice) + { + instruction[3] = 0xc0 | (tmp2_ind << 3) | 1; + sljit_emit_op_custom(compiler, instruction, 4); + + OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); + } + +/* BSF r32, r/m32 */ +instruction[0] = 0x0f; +instruction[1] = 0xbc; +instruction[2] = 0xc0 | (tmp1_ind << 3) | tmp1_ind; +sljit_emit_op_custom(compiler, instruction, 3); + +JUMPTO(SLJIT_ZERO, start); + +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); + +start = LABEL(); +SET_LABEL(quit[0], start); +SET_LABEL(quit[1], start); +SET_LABEL(quit[2], start); +} + +#undef SSE2_COMPARE_TYPE_INDEX + +#endif + +static void fast_forward_first_char2(compiler_common *common, PCRE2_UCHAR char1, PCRE2_UCHAR char2, sljit_si offset) { DEFINE_COMPILER; struct sljit_label *start; struct sljit_jump *quit; -sljit_ui chars[MAX_N_CHARS * 2]; -sljit_ub bytes[MAX_N_CHARS * MAX_N_BYTES]; -sljit_ub ones[MAX_N_CHARS]; -int offsets[3]; -sljit_ui mask; -sljit_ub *byte_set, *byte_set_end; -int i, max, from; -int range_right = -1, range_len = 3 - 1; -sljit_ub *update_table = NULL; -BOOL in_range; +struct sljit_jump *found; +PCRE2_UCHAR mask; +#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 +struct sljit_label *utf_start = NULL; +struct sljit_jump *utf_quit = NULL; +#endif +BOOL has_match_end = (common->match_end_ptr != 0); -for (i = 0; i < MAX_N_CHARS; i++) +if (offset > 0) + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset)); + +if (has_match_end) { - chars[i << 1] = NOTACHAR; - chars[(i << 1) + 1] = 0; - bytes[i * MAX_N_BYTES] = 0; - } + OP1(SLJIT_MOV, TMP3, 0, STR_END, 0); -max = scan_prefix(common, common->start, chars, bytes, MAX_N_CHARS); - -if (max <= 1) - return FALSE; - -for (i = 0; i < max; i++) - { - mask = chars[(i << 1) + 1]; - ones[i] = ones_in_half_byte[mask & 0xf]; - mask >>= 4; - while (mask != 0) + OP2(SLJIT_ADD, STR_END, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr, SLJIT_IMM, IN_UCHARS(offset + 1)); +#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) + if (sljit_x86_is_cmov_available()) { - ones[i] += ones_in_half_byte[mask & 0xf]; - mask >>= 4; + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, STR_END, 0, TMP3, 0); + sljit_x86_emit_cmov(compiler, SLJIT_GREATER, STR_END, TMP3, 0); + } +#endif + { + quit = CMP(SLJIT_LESS_EQUAL, STR_END, 0, TMP3, 0); + OP1(SLJIT_MOV, STR_END, 0, TMP3, 0); + JUMPHERE(quit); } } +#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 +if (common->utf && offset > 0) + utf_start = LABEL(); +#endif + +#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) + +/* SSE2 accelerated first character search. */ + +if (sljit_x86_is_sse2_available()) + { + fast_forward_first_char2_sse2(common, char1, char2); + + SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE || offset == 0); + if (common->mode == PCRE2_JIT_COMPLETE) + { + /* In complete mode, we don't need to run a match when STR_PTR == STR_END. */ + SLJIT_ASSERT(common->forced_quit_label == NULL); + OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_NOMATCH); + add_jump(compiler, &common->forced_quit, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0)); + +#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 + if (common->utf && offset > 0) + { + SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE); + + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-offset)); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +#if PCRE2_CODE_UNIT_WIDTH == 8 + OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc0); + CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0x80, utf_start); +#elif PCRE2_CODE_UNIT_WIDTH == 16 + OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00); + CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0xdc00, utf_start); +#else +#error "Unknown code width" +#endif + OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + } +#endif + + if (offset > 0) + OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset)); + } + else if (sljit_x86_is_cmov_available()) + { + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, STR_PTR, 0, STR_END, 0); + sljit_x86_emit_cmov(compiler, SLJIT_GREATER_EQUAL, STR_PTR, has_match_end ? SLJIT_MEM1(SLJIT_SP) : STR_END, has_match_end ? common->match_end_ptr : 0); + } + else + { + quit = CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0); + OP1(SLJIT_MOV, STR_PTR, 0, has_match_end ? SLJIT_MEM1(SLJIT_SP) : STR_END, has_match_end ? common->match_end_ptr : 0); + JUMPHERE(quit); + } + + if (has_match_end) + OP1(SLJIT_MOV, STR_END, 0, TMP3, 0); + return; + } + +#endif + +quit = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); + +start = LABEL(); +OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); + +if (char1 == char2) + found = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, char1); +else + { + mask = char1 ^ char2; + if (is_powerof2(mask)) + { + OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, mask); + found = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, char1 | mask); + } + else + { + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, char1); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, char2); + OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL); + found = JUMP(SLJIT_NOT_ZERO); + } + } + +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +CMPTO(SLJIT_LESS, STR_PTR, 0, STR_END, 0, start); + +#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 +if (common->utf && offset > 0) + utf_quit = JUMP(SLJIT_JUMP); +#endif + +JUMPHERE(found); + +#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 +if (common->utf && offset > 0) + { + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-offset)); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +#if PCRE2_CODE_UNIT_WIDTH == 8 + OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc0); + CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0x80, utf_start); +#elif PCRE2_CODE_UNIT_WIDTH == 16 + OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00); + CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0xdc00, utf_start); +#else +#error "Unknown code width" +#endif + OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + JUMPHERE(utf_quit); + } +#endif + +JUMPHERE(quit); + +if (has_match_end) + { + quit = CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr); + if (offset > 0) + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset)); + JUMPHERE(quit); + OP1(SLJIT_MOV, STR_END, 0, TMP3, 0); + } + +if (offset > 0) + OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset)); +} + +static SLJIT_INLINE BOOL fast_forward_first_n_chars(compiler_common *common) +{ +DEFINE_COMPILER; +struct sljit_label *start; +struct sljit_jump *quit; +struct sljit_jump *match; +/* bytes[0] represent the number of characters between 0 +and MAX_N_BYTES - 1, 255 represents any character. */ +PCRE2_UCHAR chars[MAX_N_CHARS * MAX_DIFF_CHARS]; +sljit_si offset; +PCRE2_UCHAR mask; +PCRE2_UCHAR *char_set, *char_set_end; +int i, max, from; +int range_right = -1, range_len; +sljit_ub *update_table = NULL; +BOOL in_range; +uint32_t rec_count; + +for (i = 0; i < MAX_N_CHARS; i++) + chars[i * MAX_DIFF_CHARS] = 0; + +rec_count = 10000; +max = scan_prefix(common, common->start, chars, MAX_N_CHARS, &rec_count); + +if (max < 1) + return FALSE; + in_range = FALSE; -from = 0; /* Prevent compiler "uninitialized" warning */ +/* Prevent compiler "uninitialized" warning */ +from = 0; +range_len = 4 /* minimum length */ - 1; for (i = 0; i <= max; i++) { - if (in_range && (i - from) > range_len && (bytes[(i - 1) * MAX_N_BYTES] <= 4)) + if (in_range && (i - from) > range_len && (chars[(i - 1) * MAX_DIFF_CHARS] < 255)) { range_len = i - from; range_right = i - 1; } - if (i < max && bytes[i * MAX_N_BYTES] < 255) + if (i < max && chars[i * MAX_DIFF_CHARS] < 255) { + SLJIT_ASSERT(chars[i * MAX_DIFF_CHARS] > 0); if (!in_range) { in_range = TRUE; from = i; } } - else if (in_range) + else in_range = FALSE; } @@ -3628,90 +4385,66 @@ if (range_right >= 0) for (i = 0; i < range_len; i++) { - byte_set = bytes + ((range_right - i) * MAX_N_BYTES); - SLJIT_ASSERT(byte_set[0] > 0 && byte_set[0] < 255); - byte_set_end = byte_set + byte_set[0]; - byte_set++; - while (byte_set <= byte_set_end) + char_set = chars + ((range_right - i) * MAX_DIFF_CHARS); + SLJIT_ASSERT(char_set[0] > 0 && char_set[0] < 255); + char_set_end = char_set + char_set[0]; + char_set++; + while (char_set <= char_set_end) { - if (update_table[*byte_set] > IN_UCHARS(i)) - update_table[*byte_set] = IN_UCHARS(i); - byte_set++; + if (update_table[(*char_set) & 0xff] > IN_UCHARS(i)) + update_table[(*char_set) & 0xff] = IN_UCHARS(i); + char_set++; } } } -offsets[0] = -1; -offsets[1] = -1; -offsets[2] = -1; +offset = -1; /* Scan forward. */ for (i = 0; i < max; i++) - if (ones[i] <= 2) { - offsets[0] = i; - break; - } - -if (offsets[0] < 0 && range_right < 0) - return FALSE; - -if (offsets[0] >= 0) { - /* Scan backward. */ - for (i = max - 1; i > offsets[0]; i--) - if (ones[i] <= 2 && i != range_right) - { - offsets[1] = i; - break; - } - - /* This case is handled better by fast_forward_first_char. */ - if (offsets[1] == -1 && offsets[0] == 0 && range_right < 0) - return FALSE; - - /* We only search for a middle character if there is no range check. */ - if (offsets[1] >= 0 && range_right == -1) + if (offset == -1) { - /* Scan from middle. */ - for (i = (offsets[0] + offsets[1]) / 2 + 1; i < offsets[1]; i++) - if (ones[i] <= 2) + if (chars[i * MAX_DIFF_CHARS] <= 2) + offset = i; + } + else if (chars[offset * MAX_DIFF_CHARS] == 2 && chars[i * MAX_DIFF_CHARS] <= 2) + { + if (chars[i * MAX_DIFF_CHARS] == 1) + offset = i; + else + { + mask = chars[offset * MAX_DIFF_CHARS + 1] ^ chars[offset * MAX_DIFF_CHARS + 2]; + if (!is_powerof2(mask)) { - offsets[2] = i; - break; + mask = chars[i * MAX_DIFF_CHARS + 1] ^ chars[i * MAX_DIFF_CHARS + 2]; + if (is_powerof2(mask)) + offset = i; } - - if (offsets[2] == -1) - { - for (i = (offsets[0] + offsets[1]) / 2; i > offsets[0]; i--) - if (ones[i] <= 2) - { - offsets[2] = i; - break; - } } } - - SLJIT_ASSERT(offsets[1] == -1 || (offsets[0] < offsets[1])); - SLJIT_ASSERT(offsets[2] == -1 || (offsets[0] < offsets[2] && offsets[1] > offsets[2])); - - chars[0] = chars[offsets[0] << 1]; - chars[1] = chars[(offsets[0] << 1) + 1]; - if (offsets[2] >= 0) - { - chars[2] = chars[offsets[2] << 1]; - chars[3] = chars[(offsets[2] << 1) + 1]; - } - if (offsets[1] >= 0) - { - chars[4] = chars[offsets[1] << 1]; - chars[5] = chars[(offsets[1] << 1) + 1]; - } } +if (range_right < 0) + { + if (offset < 0) + return FALSE; + SLJIT_ASSERT(chars[offset * MAX_DIFF_CHARS] >= 1 && chars[offset * MAX_DIFF_CHARS] <= 2); + /* Works regardless the value is 1 or 2. */ + mask = chars[offset * MAX_DIFF_CHARS + chars[offset * MAX_DIFF_CHARS]]; + fast_forward_first_char2(common, chars[offset * MAX_DIFF_CHARS + 1], mask, offset); + return TRUE; + } + +if (range_right == offset) + offset = -1; + +SLJIT_ASSERT(offset == -1 || (chars[offset * MAX_DIFF_CHARS] >= 1 && chars[offset * MAX_DIFF_CHARS] <= 2)); + max -= 1; -if (firstline) +SLJIT_ASSERT(max > 0); +if (common->match_end_ptr != 0) { - SLJIT_ASSERT(common->first_line_end != 0); - OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->first_line_end); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr); OP1(SLJIT_MOV, TMP3, 0, STR_END, 0); OP2(SLJIT_SUB, STR_END, 0, STR_END, 0, SLJIT_IMM, IN_UCHARS(max)); quit = CMP(SLJIT_LESS_EQUAL, STR_END, 0, TMP1, 0); @@ -3721,68 +4454,86 @@ if (firstline) else OP2(SLJIT_SUB, STR_END, 0, STR_END, 0, SLJIT_IMM, IN_UCHARS(max)); +SLJIT_ASSERT(range_right >= 0); + #if !(defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) -if (range_right >= 0) - OP1(SLJIT_MOV, RETURN_ADDR, 0, SLJIT_IMM, (sljit_sw)update_table); +OP1(SLJIT_MOV, RETURN_ADDR, 0, SLJIT_IMM, (sljit_sw)update_table); #endif start = LABEL(); quit = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); -SLJIT_ASSERT(range_right >= 0 || offsets[0] >= 0); - -if (range_right >= 0) - { #if PCRE2_CODE_UNIT_WIDTH == 8 || (defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN) - OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(range_right)); +OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(range_right)); #else - OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(range_right + 1) - 1); +OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(range_right + 1) - 1); #endif #if !(defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) - OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM2(RETURN_ADDR, TMP1), 0); +OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM2(RETURN_ADDR, TMP1), 0); #else - OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)update_table); +OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)update_table); #endif - OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); - CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0, start); - } +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); +CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0, start); -if (offsets[0] >= 0) +if (offset >= 0) { - OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(offsets[0])); - if (offsets[1] >= 0) - OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(offsets[1])); + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(offset)); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); - if (chars[1] != 0) - OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, chars[1]); - CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[0], start); - if (offsets[2] >= 0) - OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(offsets[2] - 1)); - - if (offsets[1] >= 0) + if (chars[offset * MAX_DIFF_CHARS] == 1) + CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset * MAX_DIFF_CHARS + 1], start); + else { - if (chars[5] != 0) - OP2(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_IMM, chars[5]); - CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, chars[4], start); + mask = chars[offset * MAX_DIFF_CHARS + 1] ^ chars[offset * MAX_DIFF_CHARS + 2]; + if (is_powerof2(mask)) + { + OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, mask); + CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset * MAX_DIFF_CHARS + 1] | mask, start); + } + else + { + match = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset * MAX_DIFF_CHARS + 1]); + CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset * MAX_DIFF_CHARS + 2], start); + JUMPHERE(match); + } } - - if (offsets[2] >= 0) - { - if (chars[3] != 0) - OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, chars[3]); - CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[2], start); - } - OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); } +#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 +if (common->utf && offset != 0) + { + if (offset < 0) + { + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + } + else + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1)); +#if PCRE2_CODE_UNIT_WIDTH == 8 + OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc0); + CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0x80, start); +#elif PCRE2_CODE_UNIT_WIDTH == 16 + OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00); + CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0xdc00, start); +#else +#error "Unknown code width" +#endif + if (offset < 0) + OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + } +#endif + +if (offset >= 0) + OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + JUMPHERE(quit); -if (firstline) +if (common->match_end_ptr != 0) { if (range_right >= 0) - OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->first_line_end); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr); OP1(SLJIT_MOV, STR_END, 0, TMP3, 0); if (range_right >= 0) { @@ -3799,24 +4550,9 @@ return TRUE; #undef MAX_N_CHARS #undef MAX_N_BYTES -static SLJIT_INLINE void fast_forward_first_char(compiler_common *common, PCRE2_UCHAR first_char, BOOL caseless, BOOL firstline) +static SLJIT_INLINE void fast_forward_first_char(compiler_common *common, PCRE2_UCHAR first_char, BOOL caseless) { -DEFINE_COMPILER; -struct sljit_label *start; -struct sljit_jump *quit; -struct sljit_jump *found; -PCRE2_UCHAR oc, bit; - -if (firstline) - { - SLJIT_ASSERT(common->first_line_end != 0); - OP1(SLJIT_MOV, TMP3, 0, STR_END, 0); - OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_SP), common->first_line_end); - } - -start = LABEL(); -quit = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); -OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); +PCRE2_UCHAR oc; oc = first_char; if (caseless) @@ -3827,36 +4563,11 @@ if (caseless) oc = UCD_OTHERCASE(first_char); #endif } -if (first_char == oc) - found = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, first_char); -else - { - bit = first_char ^ oc; - if (is_powerof2(bit)) - { - OP2(SLJIT_OR, TMP2, 0, TMP1, 0, SLJIT_IMM, bit); - found = CMP(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, first_char | bit); - } - else - { - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, first_char); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, oc); - OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL); - found = JUMP(SLJIT_NOT_ZERO); - } - } -OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); -JUMPTO(SLJIT_JUMP, start); -JUMPHERE(found); -JUMPHERE(quit); - -if (firstline) - OP1(SLJIT_MOV, STR_END, 0, TMP3, 0); +fast_forward_first_char2(common, first_char, oc, 0); } -static SLJIT_INLINE void fast_forward_newline(compiler_common *common, BOOL firstline) +static SLJIT_INLINE void fast_forward_newline(compiler_common *common) { DEFINE_COMPILER; struct sljit_label *loop; @@ -3867,11 +4578,10 @@ struct sljit_jump *foundcr = NULL; struct sljit_jump *notfoundnl; jump_list *newline = NULL; -if (firstline) +if (common->match_end_ptr != 0) { - SLJIT_ASSERT(common->first_line_end != 0); OP1(SLJIT_MOV, TMP3, 0, STR_END, 0); - OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_SP), common->first_line_end); + OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr); } if (common->nltype == NLTYPE_FIXED && common->newline > 255) @@ -3902,7 +4612,7 @@ if (common->nltype == NLTYPE_FIXED && common->newline > 255) JUMPHERE(firstchar); JUMPHERE(lastchar); - if (firstline) + if (common->match_end_ptr != 0) OP1(SLJIT_MOV, STR_END, 0, TMP3, 0); return; } @@ -3940,13 +4650,13 @@ if (common->nltype == NLTYPE_ANY || common->nltype == NLTYPE_ANYCRLF) JUMPHERE(lastchar); JUMPHERE(firstchar); -if (firstline) +if (common->match_end_ptr != 0) OP1(SLJIT_MOV, STR_END, 0, TMP3, 0); } static BOOL check_class_ranges(compiler_common *common, const sljit_ub *bits, BOOL nclass, BOOL invert, jump_list **backtracks); -static SLJIT_INLINE void fast_forward_start_bits(compiler_common *common, const sljit_ub *start_bits, BOOL firstline) +static SLJIT_INLINE void fast_forward_start_bits(compiler_common *common, const sljit_ub *start_bits) { DEFINE_COMPILER; struct sljit_label *start; @@ -3957,11 +4667,10 @@ jump_list *matches = NULL; struct sljit_jump *jump; #endif -if (firstline) +if (common->match_end_ptr != 0) { - SLJIT_ASSERT(common->first_line_end != 0); OP1(SLJIT_MOV, RETURN_ADDR, 0, STR_END, 0); - OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_SP), common->first_line_end); + OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr); } start = LABEL(); @@ -4019,7 +4728,7 @@ if (matches != NULL) set_jumps(matches, LABEL()); JUMPHERE(quit); -if (firstline) +if (common->match_end_ptr != 0) OP1(SLJIT_MOV, STR_END, 0, RETURN_ADDR, 0); } @@ -4328,8 +5037,10 @@ switch(length) case 4: if ((ranges[1] - ranges[0]) == (ranges[3] - ranges[2]) && (ranges[0] | (ranges[2] - ranges[0])) == ranges[2] + && (ranges[1] & (ranges[2] - ranges[0])) == 0 && is_powerof2(ranges[2] - ranges[0])) { + SLJIT_ASSERT((ranges[0] & (ranges[2] - ranges[0])) == 0 && (ranges[2] & ranges[3] & (ranges[2] - ranges[0])) != 0); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[2] - ranges[0]); if (ranges[2] + 1 != ranges[3]) { @@ -4788,6 +5499,7 @@ sljit_uw typeoffset; cc++; ccbegin = cc; compares = 0; + if (cc[-1] & XCL_MAP) { min = 0; @@ -4845,9 +5557,8 @@ while (*cc != XCL_END) /* Any either accepts everything or ignored. */ if (cc[-1] == XCL_PROP) { - if (list != backtracks) - compile_char1_matchingpath(common, OP_ALLANY, cc, backtracks, FALSE); - else + compile_char1_matchingpath(common, OP_ALLANY, cc, backtracks, FALSE); + if (list == backtracks) add_jump(compiler, backtracks, JUMP(SLJIT_JUMP)); return; } @@ -4928,9 +5639,10 @@ else if ((cc[-1] & XCL_MAP) != 0) if (!check_class_ranges(common, (const sljit_ub *)cc, FALSE, TRUE, list)) { #if PCRE2_CODE_UNIT_WIDTH == 8 - SLJIT_ASSERT(common->utf); + jump = NULL; + if (common->utf) #endif - jump = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 255); + jump = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 255); OP2(SLJIT_AND, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x7); OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3); @@ -4939,7 +5651,10 @@ else if ((cc[-1] & XCL_MAP) != 0) OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); add_jump(compiler, list, JUMP(SLJIT_NOT_ZERO)); - JUMPHERE(jump); +#if PCRE2_CODE_UNIT_WIDTH == 8 + if (common->utf) +#endif + JUMPHERE(jump); } OP1(SLJIT_MOV, TMP1, 0, RETURN_ADDR, 0); @@ -5280,7 +5995,7 @@ while (*cc != XCL_END) OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL); SET_CHAR_OFFSET(0); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xff); + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x7f); OP_FLAGS(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL); SET_TYPE_OFFSET(ucp_Pc); @@ -7835,6 +8550,10 @@ while (*cc != OP_KETRPOS) OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); } + /* Even if the match is empty, we need to reset the control head. */ + if (needs_control_head) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(stack)); + if (opcode == OP_SBRAPOS || opcode == OP_SCBRAPOS) add_jump(compiler, &emptymatch, CMP(SLJIT_EQUAL, TMP1, 0, STR_PTR, 0)); @@ -7862,6 +8581,10 @@ while (*cc != OP_KETRPOS) OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), (framesize + 1) * sizeof(sljit_sw), STR_PTR, 0); } + /* Even if the match is empty, we need to reset the control head. */ + if (needs_control_head) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(stack)); + if (opcode == OP_SBRAPOS || opcode == OP_SCBRAPOS) add_jump(compiler, &emptymatch, CMP(SLJIT_EQUAL, TMP1, 0, STR_PTR, 0)); @@ -7874,9 +8597,6 @@ while (*cc != OP_KETRPOS) } } - if (needs_control_head) - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(stack)); - JUMPTO(SLJIT_JUMP, loop); flush_stubs(common); @@ -8082,6 +8802,8 @@ backtrack_common *backtrack; PCRE2_UCHAR opcode; PCRE2_UCHAR type; sljit_ui max = 0, exact; +BOOL fast_fail; +sljit_si fast_str_ptr; BOOL charpos_enabled; PCRE2_UCHAR charpos_char; unsigned int charpos_othercasebit; @@ -8098,6 +8820,19 @@ int tmp_base, tmp_offset; PUSH_BACKTRACK(sizeof(char_iterator_backtrack), cc, NULL); +fast_str_ptr = PRIVATE_DATA(cc + 1); +fast_fail = TRUE; + +SLJIT_ASSERT(common->fast_forward_bc_ptr == NULL || fast_str_ptr == 0 || cc == common->fast_forward_bc_ptr); + +if (cc == common->fast_forward_bc_ptr) + fast_fail = FALSE; +else if (common->fast_fail_start_ptr == 0) + fast_str_ptr = 0; + +SLJIT_ASSERT(common->fast_forward_bc_ptr != NULL || fast_str_ptr == 0 + || (fast_str_ptr >= common->fast_fail_start_ptr && fast_str_ptr <= common->fast_fail_end_ptr)); + cc = get_iterator_parameters(common, cc, &opcode, &type, &max, &exact, &end); if (type != OP_EXTUNI) @@ -8111,9 +8846,13 @@ else tmp_offset = POSSESSIVE0; } +if (fast_fail && fast_str_ptr != 0) + add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), fast_str_ptr)); + /* Handle fixed part first. */ if (exact > 1) { + SLJIT_ASSERT(fast_str_ptr == 0); if (common->mode == PCRE2_JIT_COMPLETE #ifdef SUPPORT_UNICODE && !common->utf @@ -8144,9 +8883,12 @@ switch(opcode) { case OP_STAR: case OP_UPTO: + SLJIT_ASSERT(fast_str_ptr == 0 || opcode == OP_STAR); + if (type == OP_ANYNL || type == OP_EXTUNI) { SLJIT_ASSERT(private_data_ptr == 0); + SLJIT_ASSERT(fast_str_ptr == 0); allocate_stack(common, 2); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); @@ -8228,6 +8970,8 @@ switch(opcode) add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_ZERO)); } compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, FALSE); + if (fast_str_ptr != 0) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), fast_str_ptr, STR_PTR, 0); JUMPHERE(jump); detect_partial_match(common, &backtrack->topbacktracks); @@ -8249,6 +8993,8 @@ switch(opcode) /* Search the last instance of charpos_char. */ label = LABEL(); compile_char1_matchingpath(common, type, cc, &no_match, FALSE); + if (fast_str_ptr != 0) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), fast_str_ptr, STR_PTR, 0); detect_partial_match(common, &no_match); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); if (charpos_othercasebit != 0) @@ -8304,6 +9050,8 @@ switch(opcode) set_jumps(no_match, LABEL()); OP1(SLJIT_MOV, STR_PTR, 0, base, offset0); + if (fast_str_ptr != 0) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), fast_str_ptr, STR_PTR, 0); } #endif else @@ -8331,6 +9079,8 @@ switch(opcode) OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); set_jumps(no_match, LABEL()); OP1(SLJIT_MOV, base, offset0, STR_PTR, 0); + if (fast_str_ptr != 0) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), fast_str_ptr, STR_PTR, 0); } } BACKTRACK_AS(char_iterator_backtrack)->matchingpath = LABEL(); @@ -8341,9 +9091,12 @@ switch(opcode) allocate_stack(common, 1); OP1(SLJIT_MOV, base, offset0, STR_PTR, 0); BACKTRACK_AS(char_iterator_backtrack)->matchingpath = LABEL(); + if (fast_str_ptr != 0) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), fast_str_ptr, STR_PTR, 0); break; case OP_MINUPTO: + SLJIT_ASSERT(fast_str_ptr == 0); if (private_data_ptr == 0) allocate_stack(common, 2); OP1(SLJIT_MOV, base, offset0, STR_PTR, 0); @@ -8353,6 +9106,7 @@ switch(opcode) case OP_QUERY: case OP_MINQUERY: + SLJIT_ASSERT(fast_str_ptr == 0); if (private_data_ptr == 0) allocate_stack(common, 1); OP1(SLJIT_MOV, base, offset0, STR_PTR, 0); @@ -8375,6 +9129,8 @@ switch(opcode) JUMPTO(SLJIT_JUMP, label); set_jumps(no_match, LABEL()); OP1(SLJIT_MOV, STR_PTR, 0, tmp_base, tmp_offset); + if (fast_str_ptr != 0) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), fast_str_ptr, STR_PTR, 0); break; } #endif @@ -8385,9 +9141,12 @@ switch(opcode) set_jumps(no_char1_match, LABEL()); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); set_jumps(no_match, LABEL()); + if (fast_str_ptr != 0) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), fast_str_ptr, STR_PTR, 0); break; case OP_POSUPTO: + SLJIT_ASSERT(fast_str_ptr == 0); #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 if (common->utf) { @@ -8416,6 +9175,7 @@ switch(opcode) break; case OP_POSQUERY: + SLJIT_ASSERT(fast_str_ptr == 0); OP1(SLJIT_MOV, tmp_base, tmp_offset, STR_PTR, 0); compile_char1_matchingpath(common, type, cc, &no_match, TRUE); OP1(SLJIT_MOV, tmp_base, tmp_offset, STR_PTR, 0); @@ -8774,8 +9534,7 @@ while (cc < ccend) OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), STR_PTR, 0); } BACKTRACK_AS(braminzero_backtrack)->matchingpath = LABEL(); - if (cc[1] > OP_ASSERTBACK_NOT) - count_match(common); + count_match(common); break; case OP_ONCE: @@ -9944,7 +10703,7 @@ static SLJIT_INLINE void compile_recurse(compiler_common *common) DEFINE_COMPILER; PCRE2_SPTR cc = common->start + common->currententry->start; PCRE2_SPTR ccbegin = cc + 1 + LINK_SIZE + (*cc == OP_BRA ? 0 : IMM2_SIZE); -PCRE2_SPTR ccend = bracketend(cc); +PCRE2_SPTR ccend = bracketend(cc) - (1 + LINK_SIZE); BOOL needs_control_head; int framesize = get_framesize(common, cc, NULL, TRUE, &needs_control_head); int private_data_size = get_private_data_copy_length(common, ccbegin, ccend, needs_control_head); @@ -9967,6 +10726,7 @@ common->currententry->entry = LABEL(); set_jumps(common->currententry->calls, common->currententry->entry); sljit_emit_fast_enter(compiler, TMP2, 0); +count_match(common); allocate_stack(common, private_data_size + framesize + alternativesize); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(private_data_size + framesize + alternativesize - 1), TMP2, 0); copy_private_data(common, ccbegin, ccend, TRUE, private_data_size + framesize + alternativesize, framesize + alternativesize, needs_control_head); @@ -10206,9 +10966,9 @@ if (mode != PCRE2_JIT_COMPLETE) common->ovector_start += sizeof(sljit_sw); } } -if ((re->overall_options & PCRE2_FIRSTLINE) != 0) +if ((re->overall_options & (PCRE2_FIRSTLINE | PCRE2_USE_OFFSET_LIMIT)) != 0) { - common->first_line_end = common->ovector_start; + common->match_end_ptr = common->ovector_start; common->ovector_start += sizeof(sljit_sw); } #if defined DEBUG_FORCE_CONTROL_HEAD && DEBUG_FORCE_CONTROL_HEAD @@ -10251,6 +11011,14 @@ memset(common->private_data_ptrs, 0, total_length * sizeof(sljit_si)); private_data_size = common->cbra_ptr + (re->top_bracket + 1) * sizeof(sljit_sw); set_private_data_ptrs(common, &private_data_size, ccend); +if ((re->overall_options & PCRE2_ANCHORED) == 0 && (re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0) + { + if (!detect_fast_forward_skip(common, &private_data_size) && !common->has_skip_in_assert_back) + detect_fast_fail(common, common->start, &private_data_size, 4); + } + +SLJIT_ASSERT(common->fast_fail_start_ptr <= common->fast_fail_end_ptr); + if (private_data_size > SLJIT_MAX_LOCAL_SIZE) { SLJIT_FREE(common->private_data_ptrs, allocator_data); @@ -10290,8 +11058,12 @@ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, stack)); OP1(SLJIT_MOV_UI, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, limit_match)); OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(struct sljit_stack, base)); OP1(SLJIT_MOV, STACK_LIMIT, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(struct sljit_stack, limit)); +OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LIMIT_MATCH, TMP1, 0); +if (common->fast_fail_start_ptr < common->fast_fail_end_ptr) + reset_fast_fail(common); + if (mode == PCRE2_JIT_PARTIAL_SOFT) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->hit_start, SLJIT_IMM, -1); if (common->mark_ptr != 0) @@ -10302,19 +11074,19 @@ if (common->control_head_ptr != 0) /* Main part of the matching */ if ((re->overall_options & PCRE2_ANCHORED) == 0) { - mainloop_label = mainloop_entry(common, (re->flags & PCRE2_HASCRORLF) != 0, (re->overall_options & PCRE2_FIRSTLINE) != 0); + mainloop_label = mainloop_entry(common, (re->flags & PCRE2_HASCRORLF) != 0, re->overall_options); continue_match_label = LABEL(); /* Forward search if possible. */ if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0) { - if (mode == PCRE2_JIT_COMPLETE && fast_forward_first_n_chars(common, (re->overall_options & PCRE2_FIRSTLINE) != 0)) + if (mode == PCRE2_JIT_COMPLETE && fast_forward_first_n_chars(common)) ; else if ((re->flags & PCRE2_FIRSTSET) != 0) - fast_forward_first_char(common, (PCRE2_UCHAR)(re->first_codeunit), (re->flags & PCRE2_FIRSTCASELESS) != 0, (re->overall_options & PCRE2_FIRSTLINE) != 0); + fast_forward_first_char(common, (PCRE2_UCHAR)(re->first_codeunit), (re->flags & PCRE2_FIRSTCASELESS) != 0); else if ((re->flags & PCRE2_STARTLINE) != 0) - fast_forward_newline(common, (re->overall_options & PCRE2_FIRSTLINE) != 0); + fast_forward_newline(common); else if ((re->flags & PCRE2_FIRSTMAPSET) != 0) - fast_forward_start_bits(common, re->start_bitmap, (re->overall_options & PCRE2_FIRSTLINE) != 0); + fast_forward_start_bits(common, re->start_bitmap); } } else @@ -10335,6 +11107,8 @@ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(0), STR_PTR, 0); OP1(SLJIT_MOV, COUNT_MATCH, 0, SLJIT_MEM1(SLJIT_SP), LIMIT_MATCH); if (common->capture_last_ptr != 0) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr, SLJIT_IMM, 0); +if (common->fast_forward_bc_ptr != NULL) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), PRIVATE_DATA(common->fast_forward_bc_ptr + 1), STR_PTR, 0); if (common->start_ptr != OVECTOR(0)) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->start_ptr, STR_PTR, 0); @@ -10413,28 +11187,37 @@ if (mode == PCRE2_JIT_PARTIAL_SOFT) } /* Check we have remaining characters. */ -if ((re->overall_options & PCRE2_ANCHORED) == 0 && (re->overall_options & PCRE2_FIRSTLINE) != 0) +if ((re->overall_options & PCRE2_ANCHORED) == 0 && common->match_end_ptr != 0) { - SLJIT_ASSERT(common->first_line_end != 0); - OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->first_line_end); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr); } -OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), common->start_ptr); +if (common->fast_forward_bc_ptr != NULL) + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), PRIVATE_DATA(common->fast_forward_bc_ptr + 1)); +else + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), common->start_ptr); if ((re->overall_options & PCRE2_ANCHORED) == 0) { if (common->ff_newline_shortcut != NULL) { + /* There cannot be more newlines if PCRE2_FIRSTLINE is set. */ if ((re->overall_options & PCRE2_FIRSTLINE) == 0) - CMPTO(SLJIT_LESS, STR_PTR, 0, STR_END, 0, common->ff_newline_shortcut); - /* There cannot be more newlines here. */ + { + if (common->match_end_ptr != 0) + { + OP1(SLJIT_MOV, TMP3, 0, STR_END, 0); + OP1(SLJIT_MOV, STR_END, 0, TMP1, 0); + CMPTO(SLJIT_LESS, STR_PTR, 0, TMP1, 0, common->ff_newline_shortcut); + OP1(SLJIT_MOV, STR_END, 0, TMP3, 0); + } + else + CMPTO(SLJIT_LESS, STR_PTR, 0, STR_END, 0, common->ff_newline_shortcut); + } } else { - if ((re->overall_options & PCRE2_FIRSTLINE) == 0) - CMPTO(SLJIT_LESS, STR_PTR, 0, STR_END, 0, mainloop_label); - else - CMPTO(SLJIT_LESS, STR_PTR, 0, TMP1, 0, mainloop_label); + CMPTO(SLJIT_LESS, STR_PTR, 0, (common->match_end_ptr == 0) ? STR_END : TMP1, 0, mainloop_label); } } @@ -10464,6 +11247,9 @@ if (common->might_be_empty) JUMPTO(SLJIT_JUMP, empty_match_backtrack_label); } +common->fast_forward_bc_ptr = NULL; +common->fast_fail_start_ptr = 0; +common->fast_fail_end_ptr = 0; common->currententry = common->entries; common->local_exit = TRUE; quit_label = common->quit_label; diff --git a/pcre2-10.20/src/pcre2_jit_match.c b/pcre2-10.21/src/pcre2_jit_match.c similarity index 97% rename from pcre2-10.20/src/pcre2_jit_match.c rename to pcre2-10.21/src/pcre2_jit_match.c index d8d941e46..d804cfea4 100644 --- a/pcre2-10.20/src/pcre2_jit_match.c +++ b/pcre2-10.21/src/pcre2_jit_match.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2014 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -129,10 +129,12 @@ arguments.match_data = match_data; arguments.startchar_ptr = subject; arguments.mark_ptr = NULL; arguments.options = options; + if (mcontext != NULL) { arguments.callout = mcontext->callout; arguments.callout_data = mcontext->callout_data; + arguments.offset_limit = mcontext->offset_limit; arguments.limit_match = (mcontext->match_limit < re->limit_match)? mcontext->match_limit : re->limit_match; if (mcontext->jit_callback != NULL) @@ -144,6 +146,7 @@ else { arguments.callout = NULL; arguments.callout_data = NULL; + arguments.offset_limit = PCRE2_UNSET; arguments.limit_match = (MATCH_LIMIT < re->limit_match)? MATCH_LIMIT : re->limit_match; jit_stack = NULL; diff --git a/pcre2-10.20/src/pcre2_jit_misc.c b/pcre2-10.21/src/pcre2_jit_misc.c similarity index 99% rename from pcre2-10.20/src/pcre2_jit_misc.c rename to pcre2-10.21/src/pcre2_jit_misc.c index f5b51286e..efdb05580 100644 --- a/pcre2-10.20/src/pcre2_jit_misc.c +++ b/pcre2-10.21/src/pcre2_jit_misc.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2014 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without diff --git a/pcre2-10.20/src/pcre2_jit_test.c b/pcre2-10.21/src/pcre2_jit_test.c similarity index 98% rename from pcre2-10.20/src/pcre2_jit_test.c rename to pcre2-10.21/src/pcre2_jit_test.c index b076c67d1..78837cf56 100644 --- a/pcre2-10.20/src/pcre2_jit_test.c +++ b/pcre2-10.21/src/pcre2_jit_test.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2014 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -188,6 +188,7 @@ static struct regression_test_case regression_test_cases[] = { { CMUP, A, 0, 0, "\xf0\x90\x90\x80{2}", "\xf0\x90\x90\x80#\xf0\x90\x90\xa8\xf0\x90\x90\x80" }, { CMUP, A, 0, 0, "\xf0\x90\x90\xa8{2}", "\xf0\x90\x90\x80#\xf0\x90\x90\xa8\xf0\x90\x90\x80" }, { CMUP, A, 0, 0, "\xe1\xbd\xb8\xe1\xbf\xb8", "\xe1\xbf\xb8\xe1\xbd\xb8" }, + { M, A, 0, 0, "[3-57-9]", "5" }, /* Assertions. */ { MU, A, 0, 0, "\\b[^A]", "A_B#" }, @@ -247,13 +248,17 @@ static struct regression_test_case regression_test_cases[] = { { M, A, 0, 0, "a\\z", "aaa" }, { M, A, 0, 0 | F_NOMATCH, "a\\z", "aab" }, - /* Brackets. */ + /* Brackets and alternatives. */ { MU, A, 0, 0, "(ab|bb|cd)", "bacde" }, { MU, A, 0, 0, "(?:ab|a)(bc|c)", "ababc" }, { MU, A, 0, 0, "((ab|(cc))|(bb)|(?:cd|efg))", "abac" }, { CMU, A, 0, 0, "((aB|(Cc))|(bB)|(?:cd|EFg))", "AcCe" }, { MU, A, 0, 0, "((ab|(cc))|(bb)|(?:cd|ebg))", "acebebg" }, { MU, A, 0, 0, "(?:(a)|(?:b))(cc|(?:d|e))(a|b)k", "accabdbbccbk" }, + { MU, A, 0, 0, "\xc7\x82|\xc6\x82", "\xf1\x83\x82\x82\xc7\x82\xc7\x83" }, + { MU, A, 0, 0, "=\xc7\x82|#\xc6\x82", "\xf1\x83\x82\x82=\xc7\x82\xc7\x83" }, + { MU, A, 0, 0, "\xc7\x82\xc7\x83|\xc6\x82\xc6\x82", "\xf1\x83\x82\x82\xc7\x82\xc7\x83" }, + { MU, A, 0, 0, "\xc6\x82\xc6\x82|\xc7\x83\xc7\x83|\xc8\x84\xc8\x84", "\xf1\x83\x82\x82\xc8\x84\xc8\x84" }, /* Greedy and non-greedy ? operators. */ { MU, A, 0, 0, "(?:a)?a", "laab" }, @@ -323,6 +328,14 @@ static struct regression_test_case regression_test_cases[] = { { CMU, A, 0, 0, "[^\xe1\xbd\xb8][^\xc3\xa9]", "\xe1\xbd\xb8\xe1\xbf\xb8\xc3\xa9\xc3\x89#" }, { MU, A, 0, 0, "[^\xe1\xbd\xb8][^\xc3\xa9]", "\xe1\xbd\xb8\xe1\xbf\xb8\xc3\xa9\xc3\x89#" }, { MU, A, 0, 0, "[^\xe1\xbd\xb8]{3,}?", "##\xe1\xbd\xb8#\xe1\xbd\xb8#\xc3\x89#\xe1\xbd\xb8" }, + { MU, A, 0, 0, "\\d+123", "987654321,01234" }, + { MU, A, 0, 0, "abcd*|\\w+xy", "aaaaa,abxyz" }, + { MU, A, 0, 0, "(?:abc|((?:amc|\\b\\w*xy)))", "aaaaa,abxyz" }, + { MU, A, 0, 0, "a(?R)|([a-z]++)#", ".abcd.abcd#."}, + { MU, A, 0, 0, "a(?R)|([a-z]++)#", ".abcd.mbcd#."}, + { MU, A, 0, 0, ".[ab]*.", "xx" }, + { MU, A, 0, 0, ".[ab]*a", "xxa" }, + { MU, A, 0, 0, ".[ab]?.", "xx" }, /* Bracket repeats with limit. */ { MU, A, 0, 0, "(?:(ab){2}){5}M", "abababababababababababM" }, @@ -679,6 +692,7 @@ static struct regression_test_case regression_test_cases[] = { { MU | PCRE2_FIRSTLINE, PCRE2_NEWLINE_CRLF, 0, 1, ".", "\r\n" }, { PCRE2_FIRSTLINE | PCRE2_DOTALL, PCRE2_NEWLINE_LF, 0, 0 | F_NOMATCH, "ab.", "ab" }, { MU | PCRE2_FIRSTLINE, A, 0, 1 | F_NOMATCH, "^[a-d0-9]", "\nxx\nd" }, + { PCRE2_FIRSTLINE | PCRE2_DOTALL, PCRE2_NEWLINE_ANY, 0, 0, "....a", "012\n0a" }, /* Recurse. */ { MU, A, 0, 0, "(a)(?1)", "aa" }, @@ -813,6 +827,9 @@ static struct regression_test_case regression_test_cases[] = { /* (*SKIP) verb. */ { MU, A, 0, 0 | F_NOMATCH, "(?=a(*SKIP)b)ab|ad", "ad" }, + { MU, A, 0, 0, "(\\w+(*SKIP)#)", "abcd,xyz#," }, + { MU, A, 0, 0, "\\w+(*SKIP)#|mm", "abcd,xyz#," }, + { MU, A, 0, 0 | F_NOMATCH, "b+(?<=(*SKIP)#c)|b+", "#bbb" }, /* (*THEN) verb. */ { MU, A, 0, 0, "((?:a(*THEN)|aab)(*THEN)c|a+)+m", "aabcaabcaabcaabcnacm" }, diff --git a/pcre2-10.20/src/pcre2_maketables.c b/pcre2-10.21/src/pcre2_maketables.c similarity index 98% rename from pcre2-10.20/src/pcre2_maketables.c rename to pcre2-10.21/src/pcre2_maketables.c index ca68bca2a..2c7ae84d8 100644 --- a/pcre2-10.20/src/pcre2_maketables.c +++ b/pcre2-10.21/src/pcre2_maketables.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2014 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without diff --git a/pcre2-10.20/src/pcre2_match.c b/pcre2-10.21/src/pcre2_match.c similarity index 98% rename from pcre2-10.20/src/pcre2_match.c rename to pcre2-10.21/src/pcre2_match.c index d3d5c1dfa..f5275c7c4 100644 --- a/pcre2-10.20/src/pcre2_match.c +++ b/pcre2-10.21/src/pcre2_match.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2014 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -194,7 +194,7 @@ if (caseless) GETCHARINC(c, eptr); GETCHARINC(d, p); ur = GET_UCD(d); - if (c != d && c != d + ur->other_case) + if (c != d && c != (uint32_t)((int)d + ur->other_case)) { const uint32_t *pp = PRIV(ucd_caseless_sets) + ur->caseset; for (;;) @@ -211,7 +211,7 @@ if (caseless) /* Not in UTF mode */ { - while (length-- > 0) + for (; length > 0; length--) { uint32_t cc, cp; if (eptr >= mb->end_subject) return 1; /* Partial match */ @@ -226,11 +226,11 @@ if (caseless) } /* In the caseful case, we can just compare the code units, whether or not we -are in UT mode. */ +are in UTF mode. */ else { - while (length-- > 0) + for (; length > 0; length--) { if (eptr >= mb->end_subject) return 1; /* Partial match */ if (UCHAR21INCTEST(p) != UCHAR21INCTEST(eptr)) return -1; /*No match */ @@ -1704,14 +1704,14 @@ for (;;) back a number of characters, not bytes. */ case OP_REVERSE: + i = GET(ecode, 1); #ifdef SUPPORT_UNICODE if (utf) { - i = GET(ecode, 1); while (i-- > 0) { + if (eptr <= mb->start_subject) RRETURN(MATCH_NOMATCH); eptr--; - if (eptr < mb->start_subject) RRETURN(MATCH_NOMATCH); BACKCHAR(eptr); } } @@ -1721,8 +1721,8 @@ for (;;) /* No UTF-8 support, or not in UTF-8 mode: count is byte count */ { - eptr -= GET(ecode, 1); - if (eptr < mb->start_subject) RRETURN(MATCH_NOMATCH); + if (i > eptr - mb->start_subject) RRETURN(MATCH_NOMATCH); + eptr -= i; } /* Save the earliest consulted character, then skip to next op code */ @@ -2408,8 +2408,9 @@ for (;;) ecode++; break; - /* Match a single byte, even in UTF-8 mode. This opcode really does match - any byte, even newline, independent of the setting of PCRE2_DOTALL. */ + /* Match a single code unit, even in UTF-8 mode. This opcode really does + match any code unit, even newline. (It really should be called ANYCODEUNIT, + of course - the byte name is from pre-16 bit days.) */ case OP_ANYBYTE: if (eptr >= mb->end_subject) /* DO NOT merge the eptr++ here; it must */ @@ -3342,7 +3343,10 @@ for (;;) CHECK_PARTIAL(); /* Not SCHECK_PARTIAL() */ RRETURN(MATCH_NOMATCH); } - while (length-- > 0) if (*ecode++ != UCHAR21INC(eptr)) RRETURN(MATCH_NOMATCH); + for (; length > 0; length--) + { + if (*ecode++ != UCHAR21INC(eptr)) RRETURN(MATCH_NOMATCH); + } } else #endif @@ -6459,6 +6463,7 @@ PCRE2_UCHAR first_cu2 = 0; PCRE2_UCHAR req_cu = 0; PCRE2_UCHAR req_cu2 = 0; +PCRE2_SPTR bumpalong_limit; PCRE2_SPTR end_subject; PCRE2_SPTR start_match = subject + start_offset; PCRE2_SPTR req_cu_ptr = start_match - 1; @@ -6482,6 +6487,7 @@ mb->match_frames_base = &frame_zero; subject string. */ if (length == PCRE2_ZERO_TERMINATED) length = PRIV(strlen)(subject); +end_subject = subject + length; /* Plausibility checks */ @@ -6513,7 +6519,7 @@ occur. */ #define FF (PCRE2_NOTEMPTY_SET|PCRE2_NE_ATST_SET) #define OO (PCRE2_NOTEMPTY|PCRE2_NOTEMPTY_ATSTART) -options |= (re->flags & FF) / ((FF & -FF) / (OO & -OO)); +options |= (re->flags & FF) / ((FF & (~FF+1)) / (OO & (~OO+1))); #undef FF #undef OO @@ -6533,21 +6539,66 @@ mb->partial = ((options & PCRE2_PARTIAL_HARD) != 0)? 2 : /* Check a UTF string for validity if required. For 8-bit and 16-bit strings, we must also check that a starting offset does not point into the middle of a -multiunit character. */ +multiunit character. We check only the portion of the subject that is going to +be inspected during matching - from the offset minus the maximum back reference +to the given length. This saves time when a small part of a large subject is +being matched by the use of a starting offset. Note that the maximum lookbehind +is a number of characters, not code units. */ #ifdef SUPPORT_UNICODE if (utf && (options & PCRE2_NO_UTF_CHECK) == 0) { - match_data->rc = PRIV(valid_utf)(subject, length, &(match_data->startchar)); - if (match_data->rc != 0) return match_data->rc; + PCRE2_SPTR check_subject = start_match; /* start_match includes offset */ + + if (start_offset > 0) + { #if PCRE2_CODE_UNIT_WIDTH != 32 - if (start_offset > 0 && start_offset < length && - NOT_FIRSTCHAR(subject[start_offset])) - return PCRE2_ERROR_BADUTFOFFSET; + unsigned int i; + if (start_match < end_subject && NOT_FIRSTCU(*start_match)) + return PCRE2_ERROR_BADUTFOFFSET; + for (i = re->max_lookbehind; i > 0 && check_subject > subject; i--) + { + check_subject--; + while (check_subject > subject && +#if PCRE2_CODE_UNIT_WIDTH == 8 + (*check_subject & 0xc0) == 0x80) +#else /* 16-bit */ + (*check_subject & 0xfc00) == 0xdc00) +#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */ + check_subject--; + } +#else + /* In the 32-bit library, one code unit equals one character. However, + we cannot just subtract the lookbehind and then compare pointers, because + a very large lookbehind could create an invalid pointer. */ + + if (start_offset >= re->max_lookbehind) + check_subject -= re->max_lookbehind; + else + check_subject = subject; #endif /* PCRE2_CODE_UNIT_WIDTH != 32 */ + } + + /* Validate the relevant portion of the subject. After an error, adjust the + offset to be an absolute offset in the whole string. */ + + match_data->rc = PRIV(valid_utf)(check_subject, + length - (check_subject - subject), &(match_data->startchar)); + if (match_data->rc != 0) + { + match_data->startchar += check_subject - subject; + return match_data->rc; + } } #endif /* SUPPORT_UNICODE */ +/* It is an error to set an offset limit without setting the flag at compile +time. */ + +if (mcontext->offset_limit != PCRE2_UNSET && + (re->overall_options & PCRE2_USE_OFFSET_LIMIT) == 0) + return PCRE2_ERROR_BADOFFSETLIMIT; + /* If the pattern was successfully studied with JIT support, run the JIT executable instead of the rest of this function. Most options must be set at compile time for the JIT code to be usable. Fallback to the normal code path if @@ -6568,30 +6619,21 @@ if (re->executable_jit != NULL && (options & ~PUBLIC_JIT_MATCH_OPTIONS) == 0) anchored = ((re->overall_options | options) & PCRE2_ANCHORED) != 0; firstline = (re->overall_options & PCRE2_FIRSTLINE) != 0; startline = (re->flags & PCRE2_STARTLINE) != 0; +bumpalong_limit = (mcontext->offset_limit == PCRE2_UNSET)? + end_subject : subject + mcontext->offset_limit; /* Fill in the fields in the match block. */ -if (mcontext == NULL) - { - mb->callout = NULL; - mb->memctl = re->memctl; +mb->callout = mcontext->callout; +mb->callout_data = mcontext->callout_data; +mb->memctl = mcontext->memctl; #ifdef HEAP_MATCH_RECURSE - mb->stack_memctl = re->memctl; +mb->stack_memctl = mcontext->stack_memctl; #endif - } -else - { - mb->callout = mcontext->callout; - mb->callout_data = mcontext->callout_data; - mb->memctl = mcontext->memctl; -#ifdef HEAP_MATCH_RECURSE - mb->stack_memctl = mcontext->stack_memctl; -#endif - } mb->start_subject = subject; mb->start_offset = start_offset; -mb->end_subject = end_subject = mb->start_subject + length; +mb->end_subject = end_subject; mb->hasthen = (re->flags & PCRE2_HASTHEN) != 0; mb->moptions = options; /* Match options */ @@ -6783,7 +6825,8 @@ for(;;) end_subject = t; } - /* Advance to a unique first code unit if there is one. */ + /* Advance to a unique first code unit if there is one. In 8-bit mode, the + use of memchr() gives a big speed up. */ if (has_first_cu) { @@ -6793,8 +6836,15 @@ for(;;) (smc = UCHAR21TEST(start_match)) != first_cu && smc != first_cu2) start_match++; else + { +#if PCRE2_CODE_UNIT_WIDTH != 8 while (start_match < end_subject && UCHAR21TEST(start_match) != first_cu) start_match++; +#else + start_match = memchr(start_match, first_cu, end_subject - start_match); + if (start_match == NULL) start_match = end_subject; +#endif + } } /* Or to just after a linebreak for a multiline match */ @@ -6926,6 +6976,14 @@ for(;;) /* ------------ End of start of match optimizations ------------ */ + /* Give no match if we have passed the bumpalong limit. */ + + if (start_match > bumpalong_limit) + { + rc = MATCH_NOMATCH; + break; + } + /* OK, we can now run the match. If "hitend" is set afterwards, remember the first starting point for which a partial match was found. */ @@ -7044,7 +7102,7 @@ for(;;) (2) The pattern is anchored or the match was failed by (*COMMIT); -(3) We are past the end of the subject; +(3) We are past the end of the subject or the bumpalong limit; (4) PCRE2_FIRSTLINE is set and we have failed to match at a newline, because this option requests that a match occur at or before the first newline in diff --git a/pcre2-10.20/src/pcre2_match_data.c b/pcre2-10.21/src/pcre2_match_data.c similarity index 98% rename from pcre2-10.20/src/pcre2_match_data.c rename to pcre2-10.21/src/pcre2_match_data.c index 1f2fb1536..85ac99834 100644 --- a/pcre2-10.20/src/pcre2_match_data.c +++ b/pcre2-10.21/src/pcre2_match_data.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2014 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without diff --git a/pcre2-10.20/src/pcre2_newline.c b/pcre2-10.21/src/pcre2_newline.c similarity index 98% rename from pcre2-10.20/src/pcre2_newline.c rename to pcre2-10.21/src/pcre2_newline.c index 7f482f245..6e9366db9 100644 --- a/pcre2-10.20/src/pcre2_newline.c +++ b/pcre2-10.21/src/pcre2_newline.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2014 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without diff --git a/pcre2-10.20/src/pcre2_ord2utf.c b/pcre2-10.21/src/pcre2_ord2utf.c similarity index 98% rename from pcre2-10.20/src/pcre2_ord2utf.c rename to pcre2-10.21/src/pcre2_ord2utf.c index d268e94ee..75252b763 100644 --- a/pcre2-10.20/src/pcre2_ord2utf.c +++ b/pcre2-10.21/src/pcre2_ord2utf.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2014 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without diff --git a/pcre2-10.20/src/pcre2_pattern_info.c b/pcre2-10.21/src/pcre2_pattern_info.c similarity index 98% rename from pcre2-10.20/src/pcre2_pattern_info.c rename to pcre2-10.21/src/pcre2_pattern_info.c index a0e734c9b..5b32a905b 100644 --- a/pcre2-10.20/src/pcre2_pattern_info.c +++ b/pcre2-10.21/src/pcre2_pattern_info.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2014 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -77,6 +77,7 @@ if (where == NULL) /* Requests field length */ case PCRE2_INFO_CAPTURECOUNT: case PCRE2_INFO_FIRSTCODETYPE: case PCRE2_INFO_FIRSTCODEUNIT: + case PCRE2_INFO_HASBACKSLASHC: case PCRE2_INFO_HASCRORLF: case PCRE2_INFO_JCHANGED: case PCRE2_INFO_LASTCODETYPE: @@ -151,6 +152,10 @@ switch(what) &(re->start_bitmap[0]) : NULL; break; + case PCRE2_INFO_HASBACKSLASHC: + *((uint32_t *)where) = (re->flags & PCRE2_HASBKC) != 0; + break; + case PCRE2_INFO_HASCRORLF: *((uint32_t *)where) = (re->flags & PCRE2_HASCRORLF) != 0; break; diff --git a/pcre2-10.20/src/pcre2_printint.c b/pcre2-10.21/src/pcre2_printint.c similarity index 95% rename from pcre2-10.20/src/pcre2_printint.c rename to pcre2-10.21/src/pcre2_printint.c index 2cd01ab63..40a633cfd 100644 --- a/pcre2-10.20/src/pcre2_printint.c +++ b/pcre2-10.21/src/pcre2_printint.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2014 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -58,12 +58,13 @@ static const char *OP_names[] = { OP_NAME_LIST }; /* The functions and tables herein must all have mode-dependent names. */ -#define OP_lengths PCRE2_SUFFIX(OP_lengths_) -#define get_ucpname PCRE2_SUFFIX(get_ucpname_) -#define pcre2_printint PCRE2_SUFFIX(pcre2_printint_) -#define print_char PCRE2_SUFFIX(print_char_) -#define print_custring PCRE2_SUFFIX(print_custring_) -#define print_prop PCRE2_SUFFIX(print_prop_) +#define OP_lengths PCRE2_SUFFIX(OP_lengths_) +#define get_ucpname PCRE2_SUFFIX(get_ucpname_) +#define pcre2_printint PCRE2_SUFFIX(pcre2_printint_) +#define print_char PCRE2_SUFFIX(print_char_) +#define print_custring PCRE2_SUFFIX(print_custring_) +#define print_custring_bylen PCRE2_SUFFIX(print_custring_bylen_) +#define print_prop PCRE2_SUFFIX(print_prop_) /* Table of sizes for the fixed-length opcodes. It's defined in a macro so that the definition is next to the definition of the opcodes in pcre2_internal.h. @@ -188,12 +189,14 @@ return 0; * Print string as a list of code units * *************************************************/ -/* This takes no account of UTF as it always prints each individual code unit. -The string is zero-terminated. +/* These take no account of UTF as they always print each individual code unit. +The string is zero-terminated for print_custring(); the length is given for +print_custring_bylen(). Arguments: f file to write to ptr point to the string + len length for print_custring_bylen() Returns: nothing */ @@ -208,6 +211,16 @@ while (*ptr != '\0') } } +static void +print_custring_bylen(FILE *f, PCRE2_SPTR ptr, PCRE2_UCHAR len) +{ +while (len-- > 0) + { + register uint32_t c = *ptr++; + if (PRINTABLE(c)) fprintf(f, "%c", c); else fprintf(f, "\\x{%x}", c); + } +} + /************************************************* @@ -603,7 +616,7 @@ for(;;) c = code[1 + 4*LINK_SIZE]; fprintf(f, " %s %c", OP_names[*code], c); extra = GET(code, 1 + 2*LINK_SIZE); - print_custring(f, code + 2 + 4*LINK_SIZE); + print_custring_bylen(f, code + 2 + 4*LINK_SIZE, extra - 3 - 4*LINK_SIZE); for (i = 0; PRIV(callout_start_delims)[i] != 0; i++) if (c == PRIV(callout_start_delims)[i]) { @@ -791,7 +804,7 @@ for(;;) case OP_SKIP_ARG: case OP_THEN_ARG: fprintf(f, " %s ", OP_names[*code]); - print_custring(f, code + 2); + print_custring_bylen(f, code + 2, code[1]); extra += code[1]; break; diff --git a/pcre2-10.20/src/pcre2_serialize.c b/pcre2-10.21/src/pcre2_serialize.c similarity index 97% rename from pcre2-10.20/src/pcre2_serialize.c rename to pcre2-10.21/src/pcre2_serialize.c index 828b9461e..8c44acfdc 100644 --- a/pcre2-10.20/src/pcre2_serialize.c +++ b/pcre2-10.21/src/pcre2_serialize.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2015 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -104,7 +104,7 @@ for (i = 0; i < number_of_codes; i++) return PCRE2_ERROR_MIXEDTABLES; total_size += re->blocksize; } - + /* Initialize the byte stream. */ bytes = memctl->malloc(total_size + sizeof(pcre2_memctl), memctl->memory_data); if (bytes == NULL) return PCRE2_ERROR_NOMEMORY; @@ -167,7 +167,7 @@ if (number_of_codes > data->number_of_codes) src_bytes = bytes + sizeof(pcre2_serialized_data); -/* Decode tables. The reference count for the tables is stored immediately +/* Decode tables. The reference count for the tables is stored immediately following them. */ tables = memctl->malloc(tables_length + sizeof(PCRE2_SIZE), memctl->memory_data); @@ -179,8 +179,8 @@ src_bytes += tables_length; /* Decode the byte stream. We must not try to read the size from the compiled code block in the stream, because it might be unaligned, which causes errors on -hardware such as Sparc-64 that doesn't like unaligned memory accesses. The type -of the blocksize field is given its own name to ensure that it is the same here +hardware such as Sparc-64 that doesn't like unaligned memory accesses. The type +of the blocksize field is given its own name to ensure that it is the same here as in the block. */ for (i = 0; i < number_of_codes; i++) @@ -190,8 +190,8 @@ for (i = 0; i < number_of_codes; i++) sizeof(CODE_BLOCKSIZE_TYPE)); /* The allocator provided by gcontext replaces the original one. */ - - dst_re = (pcre2_real_code *)PRIV(memctl_malloc)(blocksize, + + dst_re = (pcre2_real_code *)PRIV(memctl_malloc)(blocksize, (pcre2_memctl *)gcontext); if (dst_re == NULL) { @@ -205,12 +205,12 @@ for (i = 0; i < number_of_codes; i++) } /* The new allocator must be preserved. */ - + memcpy(((uint8_t *)dst_re) + sizeof(pcre2_memctl), src_bytes + sizeof(pcre2_memctl), blocksize - sizeof(pcre2_memctl)); /* At the moment only one table is supported. */ - + dst_re->tables = tables; dst_re->executable_jit = NULL; dst_re->flags |= PCRE2_DEREF_TABLES; @@ -252,7 +252,7 @@ if (bytes != NULL) { pcre2_memctl *memctl = (pcre2_memctl *)(bytes - sizeof(pcre2_memctl)); memctl->free(memctl, memctl->memory_data); - } + } } /* End of pcre2_serialize.c */ diff --git a/pcre2-10.20/src/pcre2_string_utils.c b/pcre2-10.21/src/pcre2_string_utils.c similarity index 97% rename from pcre2-10.20/src/pcre2_string_utils.c rename to pcre2-10.21/src/pcre2_string_utils.c index 888620e19..2a1f28262 100644 --- a/pcre2-10.20/src/pcre2_string_utils.c +++ b/pcre2-10.21/src/pcre2_string_utils.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2014 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -121,7 +121,7 @@ int PRIV(strncmp)(PCRE2_SPTR str1, PCRE2_SPTR str2, size_t len) { PCRE2_UCHAR c1, c2; -while (len-- > 0) +for (; len > 0; len--) { c1 = *str1++; c2 = *str2++; @@ -150,7 +150,7 @@ int PRIV(strncmp_c8)(PCRE2_SPTR str1, const char *str2, size_t len) { PCRE2_UCHAR c1, c2; -while (len-- > 0) +for (; len > 0; len--) { c1 = *str1++; c2 = *str2++; diff --git a/pcre2-10.20/src/pcre2_study.c b/pcre2-10.21/src/pcre2_study.c similarity index 88% rename from pcre2-10.20/src/pcre2_study.c rename to pcre2-10.21/src/pcre2_study.c index 25d7e5140..18932adef 100644 --- a/pcre2-10.20/src/pcre2_study.c +++ b/pcre2-10.21/src/pcre2_study.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2014 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -59,15 +59,17 @@ collecting data (e.g. minimum matching length). */ enum { SSB_FAIL, SSB_DONE, SSB_CONTINUE, SSB_UNKNOWN }; - /************************************************* * Find the minimum subject length for a group * *************************************************/ /* Scan a parenthesized group and compute the minimum length of subject that is needed to match it. This is a lower bound; it does not mean there is a -string of that length that matches. In UTF8 mode, the result is in characters -rather than bytes. +string of that length that matches. In UTF mode, the result is in characters +rather than code units. The field in a compiled pattern for storing the minimum +length is 16-bits long (on the grounds that anything longer than that is +pathological), so we give up when we reach that amount. This also means that +integer overflow for really crazy patterns cannot happen. Arguments: re compiled pattern block @@ -75,36 +77,57 @@ rather than bytes. startcode pointer to start of the whole pattern's code utf UTF flag recurses chain of recurse_check to catch mutual recursion + countptr pointer to call count (to catch over complexity) Returns: the minimum length -1 \C in UTF-8 mode or (*ACCEPT) + or pattern too complicated + or back reference to duplicate name/number -2 internal error (missing capturing bracket) -3 internal error (opcode not listed) */ static int find_minlength(const pcre2_real_code *re, PCRE2_SPTR code, - PCRE2_SPTR startcode, BOOL utf, recurse_check *recurses) + PCRE2_SPTR startcode, BOOL utf, recurse_check *recurses, int *countptr) { int length = -1; +int prev_cap_recno = -1; +int prev_cap_d = 0; +int prev_recurse_recno = -1; +int prev_recurse_d = 0; +uint32_t once_fudge = 0; BOOL had_recurse = FALSE; +BOOL dupcapused = (re->flags & PCRE2_DUPCAPUSED) != 0; recurse_check this_recurse; register int branchlength = 0; register PCRE2_UCHAR *cc = (PCRE2_UCHAR *)code + 1 + LINK_SIZE; -if (*code == OP_CBRA || *code == OP_SCBRA || - *code == OP_CBRAPOS || *code == OP_SCBRAPOS) cc += IMM2_SIZE; +/* If this is a "could be empty" group, its minimum length is 0. */ -/* Scan along the opcodes for this branch. If we get to the end of the -branch, check the length against that of the other branches. */ +if (*code >= OP_SBRA && *code <= OP_SCOND) return 0; + +/* Skip over capturing bracket number */ + +if (*code == OP_CBRA || *code == OP_CBRAPOS) cc += IMM2_SIZE; + +/* A large and/or complex regex can take too long to process. */ + +if ((*countptr)++ > 1000) return -1; + +/* Scan along the opcodes for this branch. If we get to the end of the branch, +check the length against that of the other branches. If the accumulated length +passes 16-bits, stop. */ for (;;) { - int d, min; + int d, min, recno; PCRE2_UCHAR *cs, *ce; register PCRE2_UCHAR op = *cc; + if (branchlength >= UINT16_MAX) return UINT16_MAX; + switch (op) { case OP_COND: @@ -112,7 +135,8 @@ for (;;) /* If there is only one branch in a condition, the implied branch has zero length, so we don't add anything. This covers the DEFINE "condition" - automatically. */ + automatically. If there are two branches we can treat it the same as any + other non-capturing subpattern. */ cs = cc + GET(cc, 1); if (*cs != OP_ALT) @@ -120,23 +144,52 @@ for (;;) cc = cs + 1 + LINK_SIZE; break; } + goto PROCESS_NON_CAPTURE; - /* Otherwise we can fall through and treat it the same as any other - subpattern. */ + /* There's a special case of OP_ONCE, when it is wrapped round an + OP_RECURSE. We'd like to process the latter at this level so that + remembering the value works for repeated cases. So we do nothing, but + set a fudge value to skip over the OP_KET after the recurse. */ + + case OP_ONCE: + if (cc[1+LINK_SIZE] == OP_RECURSE && cc[2*(1+LINK_SIZE)] == OP_KET) + { + once_fudge = 1 + LINK_SIZE; + cc += 1 + LINK_SIZE; + break; + } + /* Fall through */ + + case OP_ONCE_NC: + case OP_BRA: + case OP_SBRA: + case OP_BRAPOS: + case OP_SBRAPOS: + PROCESS_NON_CAPTURE: + d = find_minlength(re, cc, startcode, utf, recurses, countptr); + if (d < 0) return d; + branchlength += d; + do cc += GET(cc, 1); while (*cc == OP_ALT); + cc += 1 + LINK_SIZE; + break; + + /* To save time for repeated capturing subpatterns, we remember the + length of the previous one. Unfortunately we can't do the same for + the unnumbered ones above. Nor can we do this if (?| is present in the + pattern because captures with the same number are not then identical. */ case OP_CBRA: case OP_SCBRA: - case OP_BRA: - case OP_SBRA: case OP_CBRAPOS: case OP_SCBRAPOS: - case OP_BRAPOS: - case OP_SBRAPOS: - case OP_ONCE: - case OP_ONCE_NC: - d = find_minlength(re, cc, startcode, utf, recurses); - if (d < 0) return d; - branchlength += d; + recno = dupcapused? prev_cap_recno - 1 : (int)GET2(cc, 1+LINK_SIZE); + if (recno != prev_cap_recno) + { + prev_cap_recno = recno; + prev_cap_d = find_minlength(re, cc, startcode, utf, recurses, countptr); + if (prev_cap_d < 0) return prev_cap_d; + } + branchlength += prev_cap_d; do cc += GET(cc, 1); while (*cc == OP_ALT); cc += 1 + LINK_SIZE; break; @@ -388,8 +441,12 @@ for (;;) matches an empty string (by default it causes a matching failure), so in that case we must set the minimum length to zero. */ - case OP_DNREF: /* Duplicate named pattern back reference */ + /* Duplicate named pattern back reference. We cannot reliably find a length + for this if duplicate numbers are present in the pattern. */ + + case OP_DNREF: case OP_DNREFI: + if (dupcapused) return -1; if ((re->overall_options & PCRE2_MATCH_UNSET_BACKREF) == 0) { int count = GET2(cc, 1+IMM2_SIZE); @@ -427,7 +484,7 @@ for (;;) int dd; this_recurse.prev = recurses; this_recurse.group = cs; - dd = find_minlength(re, cs, startcode, utf, &this_recurse); + dd = find_minlength(re, cs, startcode, utf, &this_recurse, countptr); if (dd < d) d = dd; } } @@ -438,8 +495,12 @@ for (;;) cc += 1 + 2*IMM2_SIZE; goto REPEAT_BACK_REFERENCE; - case OP_REF: /* Single back reference */ + /* Single back reference. We cannot find a length for this if duplicate + numbers are present in the pattern. */ + + case OP_REF: case OP_REFI: + if (dupcapused) return -1; if ((re->overall_options & PCRE2_MATCH_UNSET_BACKREF) == 0) { ce = cs = (PCRE2_UCHAR *)PRIV(find_bracket)(startcode, utf, GET2(cc, 1)); @@ -463,7 +524,7 @@ for (;;) { this_recurse.prev = recurses; this_recurse.group = cs; - d = find_minlength(re, cs, startcode, utf, &this_recurse); + d = find_minlength(re, cs, startcode, utf, &this_recurse, countptr); } } } @@ -504,28 +565,51 @@ for (;;) break; } - branchlength += min * d; + /* Take care not to overflow: (1) min and d are ints, so check that their + product is not greater than INT_MAX. (2) branchlength is limited to + UINT16_MAX (checked at the top of the loop). */ + + if ((d > 0 && (INT_MAX/d) < min) || UINT16_MAX - branchlength < min*d) + branchlength = UINT16_MAX; + else branchlength += min * d; break; + /* Recursion always refers to the first occurrence of a subpattern with a + given number. Therefore, we can always make use of caching, even when the + pattern contains multiple subpatterns with the same number. */ + case OP_RECURSE: cs = ce = (PCRE2_UCHAR *)startcode + GET(cc, 1); - do ce += GET(ce, 1); while (*ce == OP_ALT); - if (cc > cs && cc < ce) /* Simple recursion */ - had_recurse = TRUE; + recno = GET2(cs, 1+LINK_SIZE); + if (recno == prev_recurse_recno) + { + branchlength += prev_recurse_d; + } else { - recurse_check *r = recurses; - for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break; - if (r != NULL) /* Mutual recursion */ + do ce += GET(ce, 1); while (*ce == OP_ALT); + if (cc > cs && cc < ce) /* Simple recursion */ had_recurse = TRUE; else { - this_recurse.prev = recurses; - this_recurse.group = cs; - branchlength += find_minlength(re, cs, startcode, utf, &this_recurse); + recurse_check *r = recurses; + for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break; + if (r != NULL) /* Mutual recursion */ + had_recurse = TRUE; + else + { + this_recurse.prev = recurses; + this_recurse.group = cs; + prev_recurse_d = find_minlength(re, cs, startcode, utf, &this_recurse, + countptr); + if (prev_recurse_d < 0) return prev_recurse_d; + prev_recurse_recno = recno; + branchlength += prev_recurse_d; + } } } - cc += 1 + LINK_SIZE; + cc += 1 + LINK_SIZE + once_fudge; + once_fudge = 0; break; /* Anything else does not or need not match a character. We can get the @@ -1441,6 +1525,7 @@ int PRIV(study)(pcre2_real_code *re) { int min; +int count = 0; PCRE2_UCHAR *code; BOOL utf = (re->overall_options & PCRE2_UTF) != 0; @@ -1461,22 +1546,27 @@ if ((re->overall_options & PCRE2_ANCHORED) == 0 && if (rc == SSB_DONE) re->flags |= PCRE2_FIRSTMAPSET; } -/* Find the minimum length of subject string. */ +/* Find the minimum length of subject string. If it can match an empty string, +the minimum length is already known. */ -switch(min = find_minlength(re, code, code, utf, NULL)) +if ((re->flags & PCRE2_MATCH_EMPTY) == 0) { - case -1: /* \C in UTF mode or (*ACCEPT) */ - break; /* Leave minlength unchanged (will be zero) */ + switch(min = find_minlength(re, code, code, utf, NULL, &count)) + { + case -1: /* \C in UTF mode or (*ACCEPT) or over-complex regex */ + break; /* Leave minlength unchanged (will be zero) */ - case -2: - return 2; /* missing capturing bracket */ + case -2: + return 2; /* missing capturing bracket */ - case -3: - return 3; /* unrecognized opcode */ + case -3: + return 3; /* unrecognized opcode */ - default: - re->minlength = min; - break; + default: + if (min > UINT16_MAX) min = UINT16_MAX; + re->minlength = min; + break; + } } return 0; diff --git a/pcre2-10.21/src/pcre2_substitute.c b/pcre2-10.21/src/pcre2_substitute.c new file mode 100644 index 000000000..0bf781efc --- /dev/null +++ b/pcre2-10.21/src/pcre2_substitute.c @@ -0,0 +1,850 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Original API code Copyright (c) 1997-2012 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre2_internal.h" + +#define PTR_STACK_SIZE 20 + +#define SUBSTITUTE_OPTIONS \ + (PCRE2_SUBSTITUTE_EXTENDED|PCRE2_SUBSTITUTE_GLOBAL| \ + PCRE2_SUBSTITUTE_OVERFLOW_LENGTH|PCRE2_SUBSTITUTE_UNKNOWN_UNSET| \ + PCRE2_SUBSTITUTE_UNSET_EMPTY) + + + +/************************************************* +* Find end of substitute text * +*************************************************/ + +/* In extended mode, we recognize ${name:+set text:unset text} and similar +constructions. This requires the identification of unescaped : and } +characters. This function scans for such. It must deal with nested ${ +constructions. The pointer to the text is updated, either to the required end +character, or to where an error was detected. + +Arguments: + code points to the compiled expression (for options) + ptrptr points to the pointer to the start of the text (updated) + ptrend end of the whole string + last TRUE if the last expected string (only } recognized) + +Returns: 0 on success + negative error code on failure +*/ + +static int +find_text_end(const pcre2_code *code, PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend, + BOOL last) +{ +int rc = 0; +uint32_t nestlevel = 0; +BOOL literal = FALSE; +PCRE2_SPTR ptr = *ptrptr; + +for (; ptr < ptrend; ptr++) + { + if (literal) + { + if (ptr[0] == CHAR_BACKSLASH && ptr < ptrend - 1 && ptr[1] == CHAR_E) + { + literal = FALSE; + ptr += 1; + } + } + + else if (*ptr == CHAR_RIGHT_CURLY_BRACKET) + { + if (nestlevel == 0) goto EXIT; + nestlevel--; + } + + else if (*ptr == CHAR_COLON && !last && nestlevel == 0) goto EXIT; + + else if (*ptr == CHAR_DOLLAR_SIGN) + { + if (ptr < ptrend - 1 && ptr[1] == CHAR_LEFT_CURLY_BRACKET) + { + nestlevel++; + ptr += 1; + } + } + + else if (*ptr == CHAR_BACKSLASH) + { + int erc; + int errorcode = 0; + uint32_t ch; + + if (ptr < ptrend - 1) switch (ptr[1]) + { + case CHAR_L: + case CHAR_l: + case CHAR_U: + case CHAR_u: + ptr += 1; + continue; + } + + erc = PRIV(check_escape)(&ptr, ptrend, &ch, &errorcode, + code->overall_options, FALSE, NULL); + if (errorcode != 0) + { + rc = errorcode; + goto EXIT; + } + + switch(erc) + { + case 0: /* Data character */ + case ESC_E: /* Isolated \E is ignored */ + break; + + case ESC_Q: + literal = TRUE; + break; + + default: + rc = PCRE2_ERROR_BADREPESCAPE; + goto EXIT; + } + } + } + +rc = PCRE2_ERROR_REPMISSINGBRACE; /* Terminator not found */ + +EXIT: +*ptrptr = ptr; +return rc; +} + + + +/************************************************* +* Match and substitute * +*************************************************/ + +/* This function applies a compiled re to a subject string and creates a new +string with substitutions. The first 7 arguments are the same as for +pcre2_match(). Either string length may be PCRE2_ZERO_TERMINATED. + +Arguments: + code points to the compiled expression + subject points to the subject string + length length of subject string (may contain binary zeros) + start_offset where to start in the subject string + options option bits + match_data points to a match_data block, or is NULL + context points a PCRE2 context + replacement points to the replacement string + rlength length of replacement string + buffer where to put the substituted string + blength points to length of buffer; updated to length of string + +Returns: >= 0 number of substitutions made + < 0 an error code + PCRE2_ERROR_BADREPLACEMENT means invalid use of $ +*/ + +/* This macro checks for space in the buffer before copying into it. On +overflow, either give an error immediately, or keep on, accumulating the +length. */ + +#define CHECKMEMCPY(from,length) \ + if (!overflowed && lengthleft < length) \ + { \ + if ((suboptions & PCRE2_SUBSTITUTE_OVERFLOW_LENGTH) == 0) goto NOROOM; \ + overflowed = TRUE; \ + extra_needed = length - lengthleft; \ + } \ + else if (overflowed) \ + { \ + extra_needed += length; \ + } \ + else \ + { \ + memcpy(buffer + buff_offset, from, CU2BYTES(length)); \ + buff_offset += length; \ + lengthleft -= length; \ + } + +/* Here's the function */ + +PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION +pcre2_substitute(const pcre2_code *code, PCRE2_SPTR subject, PCRE2_SIZE length, + PCRE2_SIZE start_offset, uint32_t options, pcre2_match_data *match_data, + pcre2_match_context *mcontext, PCRE2_SPTR replacement, PCRE2_SIZE rlength, + PCRE2_UCHAR *buffer, PCRE2_SIZE *blength) +{ +int rc; +int subs; +int forcecase = 0; +int forcecasereset = 0; +uint32_t ovector_count; +uint32_t goptions = 0; +uint32_t suboptions; +BOOL match_data_created = FALSE; +BOOL literal = FALSE; +BOOL overflowed = FALSE; +#ifdef SUPPORT_UNICODE +BOOL utf = (code->overall_options & PCRE2_UTF) != 0; +#endif +PCRE2_UCHAR temp[6]; +PCRE2_SPTR ptr; +PCRE2_SPTR repend; +PCRE2_SIZE extra_needed = 0; +PCRE2_SIZE buff_offset, buff_length, lengthleft, fraglength; +PCRE2_SIZE *ovector; + +buff_offset = 0; +lengthleft = buff_length = *blength; +*blength = PCRE2_UNSET; + +/* Partial matching is not valid. */ + +if ((options & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) != 0) + return PCRE2_ERROR_BADOPTION; + +/* If no match data block is provided, create one. */ + +if (match_data == NULL) + { + pcre2_general_context *gcontext = (mcontext == NULL)? + (pcre2_general_context *)code : + (pcre2_general_context *)mcontext; + match_data = pcre2_match_data_create_from_pattern(code, gcontext); + if (match_data == NULL) return PCRE2_ERROR_NOMEMORY; + match_data_created = TRUE; + } +ovector = pcre2_get_ovector_pointer(match_data); +ovector_count = pcre2_get_ovector_count(match_data); + +/* Find lengths of zero-terminated strings and the end of the replacement. */ + +if (length == PCRE2_ZERO_TERMINATED) length = PRIV(strlen)(subject); +if (rlength == PCRE2_ZERO_TERMINATED) rlength = PRIV(strlen)(replacement); +repend = replacement + rlength; + +/* Check UTF replacement string if necessary. */ + +#ifdef SUPPORT_UNICODE +if (utf && (options & PCRE2_NO_UTF_CHECK) == 0) + { + rc = PRIV(valid_utf)(replacement, rlength, &(match_data->rightchar)); + if (rc != 0) + { + match_data->leftchar = 0; + goto EXIT; + } + } +#endif /* SUPPORT_UNICODE */ + +/* Save the substitute options and remove them from the match options. */ + +suboptions = options & SUBSTITUTE_OPTIONS; +options &= ~SUBSTITUTE_OPTIONS; + +/* Copy up to the start offset */ + +CHECKMEMCPY(subject, start_offset); + +/* Loop for global substituting. */ + +subs = 0; +do + { + PCRE2_SPTR ptrstack[PTR_STACK_SIZE]; + uint32_t ptrstackptr = 0; + + rc = pcre2_match(code, subject, length, start_offset, options|goptions, + match_data, mcontext); + +#ifdef SUPPORT_UNICODE + if (utf) options |= PCRE2_NO_UTF_CHECK; /* Only need to check once */ +#endif + + /* Any error other than no match returns the error code. No match when not + doing the special after-empty-match global rematch, or when at the end of the + subject, breaks the global loop. Otherwise, advance the starting point by one + character, copying it to the output, and try again. */ + + if (rc < 0) + { + PCRE2_SIZE save_start; + + if (rc != PCRE2_ERROR_NOMATCH) goto EXIT; + if (goptions == 0 || start_offset >= length) break; + + /* Advance by one code point. Then, if CRLF is a valid newline sequence and + we have advanced into the middle of it, advance one more code point. In + other words, do not start in the middle of CRLF, even if CR and LF on their + own are valid newlines. */ + + save_start = start_offset++; + if (subject[start_offset-1] == CHAR_CR && + code->newline_convention != PCRE2_NEWLINE_CR && + code->newline_convention != PCRE2_NEWLINE_LF && + start_offset < length && + subject[start_offset] == CHAR_LF) + start_offset++; + + /* Otherwise, in UTF mode, advance past any secondary code points. */ + + else if ((code->overall_options & PCRE2_UTF) != 0) + { +#if PCRE2_CODE_UNIT_WIDTH == 8 + while (start_offset < length && (subject[start_offset] & 0xc0) == 0x80) + start_offset++; +#elif PCRE2_CODE_UNIT_WIDTH == 16 + while (start_offset < length && + (subject[start_offset] & 0xfc00) == 0xdc00) + start_offset++; +#endif + } + + /* Copy what we have advanced past, reset the special global options, and + continue to the next match. */ + + fraglength = start_offset - save_start; + CHECKMEMCPY(subject + save_start, fraglength); + goptions = 0; + continue; + } + + /* Handle a successful match. Matches that use \K to end before they start + are not supported. */ + + if (ovector[1] < ovector[0]) + { + rc = PCRE2_ERROR_BADSUBSPATTERN; + goto EXIT; + } + + /* Count substitutions with a paranoid check for integer overflow; surely no + real call to this function would ever hit this! */ + + if (subs == INT_MAX) + { + rc = PCRE2_ERROR_TOOMANYREPLACE; + goto EXIT; + } + subs++; + + /* Copy the text leading up to the match. */ + + if (rc == 0) rc = ovector_count; + fraglength = ovector[0] - start_offset; + CHECKMEMCPY(subject + start_offset, fraglength); + + /* Process the replacement string. Literal mode is set by \Q, but only in + extended mode when backslashes are being interpreted. In extended mode we + must handle nested substrings that are to be reprocessed. */ + + ptr = replacement; + for (;;) + { + uint32_t ch; + unsigned int chlen; + + /* If at the end of a nested substring, pop the stack. */ + + if (ptr >= repend) + { + if (ptrstackptr <= 0) break; /* End of replacement string */ + repend = ptrstack[--ptrstackptr]; + ptr = ptrstack[--ptrstackptr]; + continue; + } + + /* Handle the next character */ + + if (literal) + { + if (ptr[0] == CHAR_BACKSLASH && ptr < repend - 1 && ptr[1] == CHAR_E) + { + literal = FALSE; + ptr += 2; + continue; + } + goto LOADLITERAL; + } + + /* Not in literal mode. */ + + if (*ptr == CHAR_DOLLAR_SIGN) + { + int group, n; + uint32_t special = 0; + BOOL inparens; + BOOL star; + PCRE2_SIZE sublength; + PCRE2_SPTR text1_start = NULL; + PCRE2_SPTR text1_end = NULL; + PCRE2_SPTR text2_start = NULL; + PCRE2_SPTR text2_end = NULL; + PCRE2_UCHAR next; + PCRE2_UCHAR name[33]; + + if (++ptr >= repend) goto BAD; + if ((next = *ptr) == CHAR_DOLLAR_SIGN) goto LOADLITERAL; + + group = -1; + n = 0; + inparens = FALSE; + star = FALSE; + + if (next == CHAR_LEFT_CURLY_BRACKET) + { + if (++ptr >= repend) goto BAD; + next = *ptr; + inparens = TRUE; + } + + if (next == CHAR_ASTERISK) + { + if (++ptr >= repend) goto BAD; + next = *ptr; + star = TRUE; + } + + if (!star && next >= CHAR_0 && next <= CHAR_9) + { + group = next - CHAR_0; + while (++ptr < repend) + { + next = *ptr; + if (next < CHAR_0 || next > CHAR_9) break; + group = group * 10 + next - CHAR_0; + + /* A check for a number greater than the hightest captured group + is sufficient here; no need for a separate overflow check. If unknown + groups are to be treated as unset, just skip over any remaining + digits and carry on. */ + + if (group > code->top_bracket) + { + if ((suboptions & PCRE2_SUBSTITUTE_UNKNOWN_UNSET) != 0) + { + while (++ptr < repend && *ptr >= CHAR_0 && *ptr <= CHAR_9); + break; + } + else + { + rc = PCRE2_ERROR_NOSUBSTRING; + goto PTREXIT; + } + } + } + } + else + { + const uint8_t *ctypes = code->tables + ctypes_offset; + while (MAX_255(next) && (ctypes[next] & ctype_word) != 0) + { + name[n++] = next; + if (n > 32) goto BAD; + if (++ptr >= repend) break; + next = *ptr; + } + if (n == 0) goto BAD; + name[n] = 0; + } + + /* In extended mode we recognize ${name:+set text:unset text} and + ${name:-default text}. */ + + if (inparens) + { + if ((suboptions & PCRE2_SUBSTITUTE_EXTENDED) != 0 && + !star && ptr < repend - 2 && next == CHAR_COLON) + { + special = *(++ptr); + if (special != CHAR_PLUS && special != CHAR_MINUS) + { + rc = PCRE2_ERROR_BADSUBSTITUTION; + goto PTREXIT; + } + + text1_start = ++ptr; + rc = find_text_end(code, &ptr, repend, special == CHAR_MINUS); + if (rc != 0) goto PTREXIT; + text1_end = ptr; + + if (special == CHAR_PLUS && *ptr == CHAR_COLON) + { + text2_start = ++ptr; + rc = find_text_end(code, &ptr, repend, TRUE); + if (rc != 0) goto PTREXIT; + text2_end = ptr; + } + } + + else + { + if (ptr >= repend || *ptr != CHAR_RIGHT_CURLY_BRACKET) + { + rc = PCRE2_ERROR_REPMISSINGBRACE; + goto PTREXIT; + } + } + + ptr++; + } + + /* Have found a syntactically correct group number or name, or *name. + Only *MARK is currently recognized. */ + + if (star) + { + if (PRIV(strcmp_c8)(name, STRING_MARK) == 0) + { + PCRE2_SPTR mark = pcre2_get_mark(match_data); + if (mark != NULL) + { + PCRE2_SPTR mark_start = mark; + while (*mark != 0) mark++; + fraglength = mark - mark_start; + CHECKMEMCPY(mark_start, fraglength); + } + } + else goto BAD; + } + + /* Substitute the contents of a group. We don't use substring_copy + functions any more, in order to support case forcing. */ + + else + { + PCRE2_SPTR subptr, subptrend; + + /* Find a number for a named group. In case there are duplicate names, + search for the first one that is set. If the name is not found when + PCRE2_SUBSTITUTE_UNKNOWN_EMPTY is set, set the group number to a + non-existent group. */ + + if (group < 0) + { + PCRE2_SPTR first, last, entry; + rc = pcre2_substring_nametable_scan(code, name, &first, &last); + if (rc == PCRE2_ERROR_NOSUBSTRING && + (suboptions & PCRE2_SUBSTITUTE_UNKNOWN_UNSET) != 0) + { + group = code->top_bracket + 1; + } + else + { + if (rc < 0) goto PTREXIT; + for (entry = first; entry <= last; entry += rc) + { + uint32_t ng = GET2(entry, 0); + if (ng < ovector_count) + { + if (group < 0) group = ng; /* First in ovector */ + if (ovector[ng*2] != PCRE2_UNSET) + { + group = ng; /* First that is set */ + break; + } + } + } + + /* If group is still negative, it means we did not find a group + that is in the ovector. Just set the first group. */ + + if (group < 0) group = GET2(first, 0); + } + } + + /* We now have a group that is identified by number. Find the length of + the captured string. If a group in a non-special substitution is unset + when PCRE2_SUBSTITUTE_UNSET_EMPTY is set, substitute nothing. */ + + rc = pcre2_substring_length_bynumber(match_data, group, &sublength); + if (rc < 0) + { + if (rc == PCRE2_ERROR_NOSUBSTRING && + (suboptions & PCRE2_SUBSTITUTE_UNKNOWN_UNSET) != 0) + { + rc = PCRE2_ERROR_UNSET; + } + if (rc != PCRE2_ERROR_UNSET) goto PTREXIT; /* Non-unset errors */ + if (special == 0) /* Plain substitution */ + { + if ((suboptions & PCRE2_SUBSTITUTE_UNSET_EMPTY) != 0) continue; + goto PTREXIT; /* Else error */ + } + } + + /* If special is '+' we have a 'set' and possibly an 'unset' text, + both of which are reprocessed when used. If special is '-' we have a + default text for when the group is unset; it must be reprocessed. */ + + if (special != 0) + { + if (special == CHAR_MINUS) + { + if (rc == 0) goto LITERAL_SUBSTITUTE; + text2_start = text1_start; + text2_end = text1_end; + } + + if (ptrstackptr >= PTR_STACK_SIZE) goto BAD; + ptrstack[ptrstackptr++] = ptr; + ptrstack[ptrstackptr++] = repend; + + if (rc == 0) + { + ptr = text1_start; + repend = text1_end; + } + else + { + ptr = text2_start; + repend = text2_end; + } + continue; + } + + /* Otherwise we have a literal substitution of a group's contents. */ + + LITERAL_SUBSTITUTE: + subptr = subject + ovector[group*2]; + subptrend = subject + ovector[group*2 + 1]; + + /* Substitute a literal string, possibly forcing alphabetic case. */ + + while (subptr < subptrend) + { + GETCHARINCTEST(ch, subptr); + if (forcecase != 0) + { +#ifdef SUPPORT_UNICODE + if (utf) + { + uint32_t type = UCD_CHARTYPE(ch); + if (PRIV(ucp_gentype)[type] == ucp_L && + type != ((forcecase > 0)? ucp_Lu : ucp_Ll)) + ch = UCD_OTHERCASE(ch); + } + else +#endif + { + if (((code->tables + cbits_offset + + ((forcecase > 0)? cbit_upper:cbit_lower) + )[ch/8] & (1 << (ch%8))) == 0) + ch = (code->tables + fcc_offset)[ch]; + } + forcecase = forcecasereset; + } + +#ifdef SUPPORT_UNICODE + if (utf) chlen = PRIV(ord2utf)(ch, temp); else +#endif + { + temp[0] = ch; + chlen = 1; + } + CHECKMEMCPY(temp, chlen); + } + } + } + + /* Handle an escape sequence in extended mode. We can use check_escape() + to process \Q, \E, \c, \o, \x and \ followed by non-alphanumerics, but + the case-forcing escapes are not supported in pcre2_compile() so must be + recognized here. */ + + else if ((suboptions & PCRE2_SUBSTITUTE_EXTENDED) != 0 && + *ptr == CHAR_BACKSLASH) + { + int errorcode = 0; + + if (ptr < repend - 1) switch (ptr[1]) + { + case CHAR_L: + forcecase = forcecasereset = -1; + ptr += 2; + continue; + + case CHAR_l: + forcecase = -1; + forcecasereset = 0; + ptr += 2; + continue; + + case CHAR_U: + forcecase = forcecasereset = 1; + ptr += 2; + continue; + + case CHAR_u: + forcecase = 1; + forcecasereset = 0; + ptr += 2; + continue; + + default: + break; + } + + rc = PRIV(check_escape)(&ptr, repend, &ch, &errorcode, + code->overall_options, FALSE, NULL); + if (errorcode != 0) goto BADESCAPE; + ptr++; + + switch(rc) + { + case ESC_E: + forcecase = forcecasereset = 0; + continue; + + case ESC_Q: + literal = TRUE; + continue; + + case 0: /* Data character */ + goto LITERAL; + + default: + goto BADESCAPE; + } + } + + /* Handle a literal code unit */ + + else + { + LOADLITERAL: + GETCHARINCTEST(ch, ptr); /* Get character value, increment pointer */ + + LITERAL: + if (forcecase != 0) + { +#ifdef SUPPORT_UNICODE + if (utf) + { + uint32_t type = UCD_CHARTYPE(ch); + if (PRIV(ucp_gentype)[type] == ucp_L && + type != ((forcecase > 0)? ucp_Lu : ucp_Ll)) + ch = UCD_OTHERCASE(ch); + } + else +#endif + { + if (((code->tables + cbits_offset + + ((forcecase > 0)? cbit_upper:cbit_lower) + )[ch/8] & (1 << (ch%8))) == 0) + ch = (code->tables + fcc_offset)[ch]; + } + forcecase = forcecasereset; + } + +#ifdef SUPPORT_UNICODE + if (utf) chlen = PRIV(ord2utf)(ch, temp); else +#endif + { + temp[0] = ch; + chlen = 1; + } + CHECKMEMCPY(temp, chlen); + } /* End handling a literal code unit */ + } /* End of loop for scanning the replacement. */ + + /* The replacement has been copied to the output. Update the start offset to + point to the rest of the subject string. If we matched an empty string, + do the magic for global matches. */ + + start_offset = ovector[1]; + goptions = (ovector[0] != ovector[1])? 0 : + PCRE2_ANCHORED|PCRE2_NOTEMPTY_ATSTART; + } while ((suboptions & PCRE2_SUBSTITUTE_GLOBAL) != 0); /* Repeat "do" loop */ + +/* Copy the rest of the subject. */ + +fraglength = length - start_offset; +CHECKMEMCPY(subject + start_offset, fraglength); +temp[0] = 0; +CHECKMEMCPY(temp , 1); + +/* If overflowed is set it means the PCRE2_SUBSTITUTE_OVERFLOW_LENGTH is set, +and matching has carried on after a full buffer, in order to compute the length +needed. Otherwise, an overflow generates an immediate error return. */ + +if (overflowed) + { + rc = PCRE2_ERROR_NOMEMORY; + *blength = buff_length + extra_needed; + } + +/* After a successful execution, return the number of substitutions and set the +length of buffer used, excluding the trailing zero. */ + +else + { + rc = subs; + *blength = buff_offset - 1; + } + +EXIT: +if (match_data_created) pcre2_match_data_free(match_data); + else match_data->rc = rc; +return rc; + +NOROOM: +rc = PCRE2_ERROR_NOMEMORY; +goto EXIT; + +BAD: +rc = PCRE2_ERROR_BADREPLACEMENT; +goto PTREXIT; + +BADESCAPE: +rc = PCRE2_ERROR_BADREPESCAPE; + +PTREXIT: +*blength = (PCRE2_SIZE)(ptr - replacement); +goto EXIT; +} + +/* End of pcre2_substitute.c */ diff --git a/pcre2-10.20/src/pcre2_substring.c b/pcre2-10.21/src/pcre2_substring.c similarity index 99% rename from pcre2-10.20/src/pcre2_substring.c rename to pcre2-10.21/src/pcre2_substring.c index eb72ad7d0..58b504d5c 100644 --- a/pcre2-10.20/src/pcre2_substring.c +++ b/pcre2-10.21/src/pcre2_substring.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2014 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without diff --git a/pcre2-10.20/src/pcre2_tables.c b/pcre2-10.21/src/pcre2_tables.c similarity index 79% rename from pcre2-10.20/src/pcre2_tables.c rename to pcre2-10.21/src/pcre2_tables.c index 17e4537d4..b945ed7a7 100644 --- a/pcre2-10.20/src/pcre2_tables.c +++ b/pcre2-10.21/src/pcre2_tables.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2014 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -227,6 +227,8 @@ version. Like all other character and string literals that are compared against the regular expression pattern, we must use STR_ macros instead of literal strings to make sure that UTF-8 support works on EBCDIC platforms. */ +#define STRING_Ahom0 STR_A STR_h STR_o STR_m "\0" +#define STRING_Anatolian_Hieroglyphs0 STR_A STR_n STR_a STR_t STR_o STR_l STR_i STR_a STR_n STR_UNDERSCORE STR_H STR_i STR_e STR_r STR_o STR_g STR_l STR_y STR_p STR_h STR_s "\0" #define STRING_Any0 STR_A STR_n STR_y "\0" #define STRING_Arabic0 STR_A STR_r STR_a STR_b STR_i STR_c "\0" #define STRING_Armenian0 STR_A STR_r STR_m STR_e STR_n STR_i STR_a STR_n "\0" @@ -274,6 +276,7 @@ strings to make sure that UTF-8 support works on EBCDIC platforms. */ #define STRING_Han0 STR_H STR_a STR_n "\0" #define STRING_Hangul0 STR_H STR_a STR_n STR_g STR_u STR_l "\0" #define STRING_Hanunoo0 STR_H STR_a STR_n STR_u STR_n STR_o STR_o "\0" +#define STRING_Hatran0 STR_H STR_a STR_t STR_r STR_a STR_n "\0" #define STRING_Hebrew0 STR_H STR_e STR_b STR_r STR_e STR_w "\0" #define STRING_Hiragana0 STR_H STR_i STR_r STR_a STR_g STR_a STR_n STR_a "\0" #define STRING_Imperial_Aramaic0 STR_I STR_m STR_p STR_e STR_r STR_i STR_a STR_l STR_UNDERSCORE STR_A STR_r STR_a STR_m STR_a STR_i STR_c "\0" @@ -321,6 +324,7 @@ strings to make sure that UTF-8 support works on EBCDIC platforms. */ #define STRING_Modi0 STR_M STR_o STR_d STR_i "\0" #define STRING_Mongolian0 STR_M STR_o STR_n STR_g STR_o STR_l STR_i STR_a STR_n "\0" #define STRING_Mro0 STR_M STR_r STR_o "\0" +#define STRING_Multani0 STR_M STR_u STR_l STR_t STR_a STR_n STR_i "\0" #define STRING_Myanmar0 STR_M STR_y STR_a STR_n STR_m STR_a STR_r "\0" #define STRING_N0 STR_N "\0" #define STRING_Nabataean0 STR_N STR_a STR_b STR_a STR_t STR_a STR_e STR_a STR_n "\0" @@ -331,6 +335,7 @@ strings to make sure that UTF-8 support works on EBCDIC platforms. */ #define STRING_No0 STR_N STR_o "\0" #define STRING_Ogham0 STR_O STR_g STR_h STR_a STR_m "\0" #define STRING_Ol_Chiki0 STR_O STR_l STR_UNDERSCORE STR_C STR_h STR_i STR_k STR_i "\0" +#define STRING_Old_Hungarian0 STR_O STR_l STR_d STR_UNDERSCORE STR_H STR_u STR_n STR_g STR_a STR_r STR_i STR_a STR_n "\0" #define STRING_Old_Italic0 STR_O STR_l STR_d STR_UNDERSCORE STR_I STR_t STR_a STR_l STR_i STR_c "\0" #define STRING_Old_North_Arabian0 STR_O STR_l STR_d STR_UNDERSCORE STR_N STR_o STR_r STR_t STR_h STR_UNDERSCORE STR_A STR_r STR_a STR_b STR_i STR_a STR_n "\0" #define STRING_Old_Permic0 STR_O STR_l STR_d STR_UNDERSCORE STR_P STR_e STR_r STR_m STR_i STR_c "\0" @@ -362,6 +367,7 @@ strings to make sure that UTF-8 support works on EBCDIC platforms. */ #define STRING_Sharada0 STR_S STR_h STR_a STR_r STR_a STR_d STR_a "\0" #define STRING_Shavian0 STR_S STR_h STR_a STR_v STR_i STR_a STR_n "\0" #define STRING_Siddham0 STR_S STR_i STR_d STR_d STR_h STR_a STR_m "\0" +#define STRING_SignWriting0 STR_S STR_i STR_g STR_n STR_W STR_r STR_i STR_t STR_i STR_n STR_g "\0" #define STRING_Sinhala0 STR_S STR_i STR_n STR_h STR_a STR_l STR_a "\0" #define STRING_Sk0 STR_S STR_k "\0" #define STRING_Sm0 STR_S STR_m "\0" @@ -398,6 +404,8 @@ strings to make sure that UTF-8 support works on EBCDIC platforms. */ #define STRING_Zs0 STR_Z STR_s "\0" const char PRIV(utt_names)[] = + STRING_Ahom0 + STRING_Anatolian_Hieroglyphs0 STRING_Any0 STRING_Arabic0 STRING_Armenian0 @@ -445,6 +453,7 @@ const char PRIV(utt_names)[] = STRING_Han0 STRING_Hangul0 STRING_Hanunoo0 + STRING_Hatran0 STRING_Hebrew0 STRING_Hiragana0 STRING_Imperial_Aramaic0 @@ -492,6 +501,7 @@ const char PRIV(utt_names)[] = STRING_Modi0 STRING_Mongolian0 STRING_Mro0 + STRING_Multani0 STRING_Myanmar0 STRING_N0 STRING_Nabataean0 @@ -502,6 +512,7 @@ const char PRIV(utt_names)[] = STRING_No0 STRING_Ogham0 STRING_Ol_Chiki0 + STRING_Old_Hungarian0 STRING_Old_Italic0 STRING_Old_North_Arabian0 STRING_Old_Permic0 @@ -533,6 +544,7 @@ const char PRIV(utt_names)[] = STRING_Sharada0 STRING_Shavian0 STRING_Siddham0 + STRING_SignWriting0 STRING_Sinhala0 STRING_Sk0 STRING_Sm0 @@ -569,175 +581,181 @@ const char PRIV(utt_names)[] = STRING_Zs0; const ucp_type_table PRIV(utt)[] = { - { 0, PT_ANY, 0 }, - { 4, PT_SC, ucp_Arabic }, - { 11, PT_SC, ucp_Armenian }, - { 20, PT_SC, ucp_Avestan }, - { 28, PT_SC, ucp_Balinese }, - { 37, PT_SC, ucp_Bamum }, - { 43, PT_SC, ucp_Bassa_Vah }, - { 53, PT_SC, ucp_Batak }, - { 59, PT_SC, ucp_Bengali }, - { 67, PT_SC, ucp_Bopomofo }, - { 76, PT_SC, ucp_Brahmi }, - { 83, PT_SC, ucp_Braille }, - { 91, PT_SC, ucp_Buginese }, - { 100, PT_SC, ucp_Buhid }, - { 106, PT_GC, ucp_C }, - { 108, PT_SC, ucp_Canadian_Aboriginal }, - { 128, PT_SC, ucp_Carian }, - { 135, PT_SC, ucp_Caucasian_Albanian }, - { 154, PT_PC, ucp_Cc }, - { 157, PT_PC, ucp_Cf }, - { 160, PT_SC, ucp_Chakma }, - { 167, PT_SC, ucp_Cham }, - { 172, PT_SC, ucp_Cherokee }, - { 181, PT_PC, ucp_Cn }, - { 184, PT_PC, ucp_Co }, - { 187, PT_SC, ucp_Common }, - { 194, PT_SC, ucp_Coptic }, - { 201, PT_PC, ucp_Cs }, - { 204, PT_SC, ucp_Cuneiform }, - { 214, PT_SC, ucp_Cypriot }, - { 222, PT_SC, ucp_Cyrillic }, - { 231, PT_SC, ucp_Deseret }, - { 239, PT_SC, ucp_Devanagari }, - { 250, PT_SC, ucp_Duployan }, - { 259, PT_SC, ucp_Egyptian_Hieroglyphs }, - { 280, PT_SC, ucp_Elbasan }, - { 288, PT_SC, ucp_Ethiopic }, - { 297, PT_SC, ucp_Georgian }, - { 306, PT_SC, ucp_Glagolitic }, - { 317, PT_SC, ucp_Gothic }, - { 324, PT_SC, ucp_Grantha }, - { 332, PT_SC, ucp_Greek }, - { 338, PT_SC, ucp_Gujarati }, - { 347, PT_SC, ucp_Gurmukhi }, - { 356, PT_SC, ucp_Han }, - { 360, PT_SC, ucp_Hangul }, - { 367, PT_SC, ucp_Hanunoo }, - { 375, PT_SC, ucp_Hebrew }, - { 382, PT_SC, ucp_Hiragana }, - { 391, PT_SC, ucp_Imperial_Aramaic }, - { 408, PT_SC, ucp_Inherited }, - { 418, PT_SC, ucp_Inscriptional_Pahlavi }, - { 440, PT_SC, ucp_Inscriptional_Parthian }, - { 463, PT_SC, ucp_Javanese }, - { 472, PT_SC, ucp_Kaithi }, - { 479, PT_SC, ucp_Kannada }, - { 487, PT_SC, ucp_Katakana }, - { 496, PT_SC, ucp_Kayah_Li }, - { 505, PT_SC, ucp_Kharoshthi }, - { 516, PT_SC, ucp_Khmer }, - { 522, PT_SC, ucp_Khojki }, - { 529, PT_SC, ucp_Khudawadi }, - { 539, PT_GC, ucp_L }, - { 541, PT_LAMP, 0 }, - { 544, PT_SC, ucp_Lao }, - { 548, PT_SC, ucp_Latin }, - { 554, PT_SC, ucp_Lepcha }, - { 561, PT_SC, ucp_Limbu }, - { 567, PT_SC, ucp_Linear_A }, - { 576, PT_SC, ucp_Linear_B }, - { 585, PT_SC, ucp_Lisu }, - { 590, PT_PC, ucp_Ll }, - { 593, PT_PC, ucp_Lm }, - { 596, PT_PC, ucp_Lo }, - { 599, PT_PC, ucp_Lt }, - { 602, PT_PC, ucp_Lu }, - { 605, PT_SC, ucp_Lycian }, - { 612, PT_SC, ucp_Lydian }, - { 619, PT_GC, ucp_M }, - { 621, PT_SC, ucp_Mahajani }, - { 630, PT_SC, ucp_Malayalam }, - { 640, PT_SC, ucp_Mandaic }, - { 648, PT_SC, ucp_Manichaean }, - { 659, PT_PC, ucp_Mc }, - { 662, PT_PC, ucp_Me }, - { 665, PT_SC, ucp_Meetei_Mayek }, - { 678, PT_SC, ucp_Mende_Kikakui }, - { 692, PT_SC, ucp_Meroitic_Cursive }, - { 709, PT_SC, ucp_Meroitic_Hieroglyphs }, - { 730, PT_SC, ucp_Miao }, - { 735, PT_PC, ucp_Mn }, - { 738, PT_SC, ucp_Modi }, - { 743, PT_SC, ucp_Mongolian }, - { 753, PT_SC, ucp_Mro }, - { 757, PT_SC, ucp_Myanmar }, - { 765, PT_GC, ucp_N }, - { 767, PT_SC, ucp_Nabataean }, - { 777, PT_PC, ucp_Nd }, - { 780, PT_SC, ucp_New_Tai_Lue }, - { 792, PT_SC, ucp_Nko }, - { 796, PT_PC, ucp_Nl }, - { 799, PT_PC, ucp_No }, - { 802, PT_SC, ucp_Ogham }, - { 808, PT_SC, ucp_Ol_Chiki }, - { 817, PT_SC, ucp_Old_Italic }, - { 828, PT_SC, ucp_Old_North_Arabian }, - { 846, PT_SC, ucp_Old_Permic }, - { 857, PT_SC, ucp_Old_Persian }, - { 869, PT_SC, ucp_Old_South_Arabian }, - { 887, PT_SC, ucp_Old_Turkic }, - { 898, PT_SC, ucp_Oriya }, - { 904, PT_SC, ucp_Osmanya }, - { 912, PT_GC, ucp_P }, - { 914, PT_SC, ucp_Pahawh_Hmong }, - { 927, PT_SC, ucp_Palmyrene }, - { 937, PT_SC, ucp_Pau_Cin_Hau }, - { 949, PT_PC, ucp_Pc }, - { 952, PT_PC, ucp_Pd }, - { 955, PT_PC, ucp_Pe }, - { 958, PT_PC, ucp_Pf }, - { 961, PT_SC, ucp_Phags_Pa }, - { 970, PT_SC, ucp_Phoenician }, - { 981, PT_PC, ucp_Pi }, - { 984, PT_PC, ucp_Po }, - { 987, PT_PC, ucp_Ps }, - { 990, PT_SC, ucp_Psalter_Pahlavi }, - { 1006, PT_SC, ucp_Rejang }, - { 1013, PT_SC, ucp_Runic }, - { 1019, PT_GC, ucp_S }, - { 1021, PT_SC, ucp_Samaritan }, - { 1031, PT_SC, ucp_Saurashtra }, - { 1042, PT_PC, ucp_Sc }, - { 1045, PT_SC, ucp_Sharada }, - { 1053, PT_SC, ucp_Shavian }, - { 1061, PT_SC, ucp_Siddham }, - { 1069, PT_SC, ucp_Sinhala }, - { 1077, PT_PC, ucp_Sk }, - { 1080, PT_PC, ucp_Sm }, - { 1083, PT_PC, ucp_So }, - { 1086, PT_SC, ucp_Sora_Sompeng }, - { 1099, PT_SC, ucp_Sundanese }, - { 1109, PT_SC, ucp_Syloti_Nagri }, - { 1122, PT_SC, ucp_Syriac }, - { 1129, PT_SC, ucp_Tagalog }, - { 1137, PT_SC, ucp_Tagbanwa }, - { 1146, PT_SC, ucp_Tai_Le }, - { 1153, PT_SC, ucp_Tai_Tham }, - { 1162, PT_SC, ucp_Tai_Viet }, - { 1171, PT_SC, ucp_Takri }, - { 1177, PT_SC, ucp_Tamil }, - { 1183, PT_SC, ucp_Telugu }, - { 1190, PT_SC, ucp_Thaana }, - { 1197, PT_SC, ucp_Thai }, - { 1202, PT_SC, ucp_Tibetan }, - { 1210, PT_SC, ucp_Tifinagh }, - { 1219, PT_SC, ucp_Tirhuta }, - { 1227, PT_SC, ucp_Ugaritic }, - { 1236, PT_SC, ucp_Vai }, - { 1240, PT_SC, ucp_Warang_Citi }, - { 1252, PT_ALNUM, 0 }, - { 1256, PT_PXSPACE, 0 }, - { 1260, PT_SPACE, 0 }, - { 1264, PT_UCNC, 0 }, - { 1268, PT_WORD, 0 }, - { 1272, PT_SC, ucp_Yi }, - { 1275, PT_GC, ucp_Z }, - { 1277, PT_PC, ucp_Zl }, - { 1280, PT_PC, ucp_Zp }, - { 1283, PT_PC, ucp_Zs } + { 0, PT_SC, ucp_Ahom }, + { 5, PT_SC, ucp_Anatolian_Hieroglyphs }, + { 27, PT_ANY, 0 }, + { 31, PT_SC, ucp_Arabic }, + { 38, PT_SC, ucp_Armenian }, + { 47, PT_SC, ucp_Avestan }, + { 55, PT_SC, ucp_Balinese }, + { 64, PT_SC, ucp_Bamum }, + { 70, PT_SC, ucp_Bassa_Vah }, + { 80, PT_SC, ucp_Batak }, + { 86, PT_SC, ucp_Bengali }, + { 94, PT_SC, ucp_Bopomofo }, + { 103, PT_SC, ucp_Brahmi }, + { 110, PT_SC, ucp_Braille }, + { 118, PT_SC, ucp_Buginese }, + { 127, PT_SC, ucp_Buhid }, + { 133, PT_GC, ucp_C }, + { 135, PT_SC, ucp_Canadian_Aboriginal }, + { 155, PT_SC, ucp_Carian }, + { 162, PT_SC, ucp_Caucasian_Albanian }, + { 181, PT_PC, ucp_Cc }, + { 184, PT_PC, ucp_Cf }, + { 187, PT_SC, ucp_Chakma }, + { 194, PT_SC, ucp_Cham }, + { 199, PT_SC, ucp_Cherokee }, + { 208, PT_PC, ucp_Cn }, + { 211, PT_PC, ucp_Co }, + { 214, PT_SC, ucp_Common }, + { 221, PT_SC, ucp_Coptic }, + { 228, PT_PC, ucp_Cs }, + { 231, PT_SC, ucp_Cuneiform }, + { 241, PT_SC, ucp_Cypriot }, + { 249, PT_SC, ucp_Cyrillic }, + { 258, PT_SC, ucp_Deseret }, + { 266, PT_SC, ucp_Devanagari }, + { 277, PT_SC, ucp_Duployan }, + { 286, PT_SC, ucp_Egyptian_Hieroglyphs }, + { 307, PT_SC, ucp_Elbasan }, + { 315, PT_SC, ucp_Ethiopic }, + { 324, PT_SC, ucp_Georgian }, + { 333, PT_SC, ucp_Glagolitic }, + { 344, PT_SC, ucp_Gothic }, + { 351, PT_SC, ucp_Grantha }, + { 359, PT_SC, ucp_Greek }, + { 365, PT_SC, ucp_Gujarati }, + { 374, PT_SC, ucp_Gurmukhi }, + { 383, PT_SC, ucp_Han }, + { 387, PT_SC, ucp_Hangul }, + { 394, PT_SC, ucp_Hanunoo }, + { 402, PT_SC, ucp_Hatran }, + { 409, PT_SC, ucp_Hebrew }, + { 416, PT_SC, ucp_Hiragana }, + { 425, PT_SC, ucp_Imperial_Aramaic }, + { 442, PT_SC, ucp_Inherited }, + { 452, PT_SC, ucp_Inscriptional_Pahlavi }, + { 474, PT_SC, ucp_Inscriptional_Parthian }, + { 497, PT_SC, ucp_Javanese }, + { 506, PT_SC, ucp_Kaithi }, + { 513, PT_SC, ucp_Kannada }, + { 521, PT_SC, ucp_Katakana }, + { 530, PT_SC, ucp_Kayah_Li }, + { 539, PT_SC, ucp_Kharoshthi }, + { 550, PT_SC, ucp_Khmer }, + { 556, PT_SC, ucp_Khojki }, + { 563, PT_SC, ucp_Khudawadi }, + { 573, PT_GC, ucp_L }, + { 575, PT_LAMP, 0 }, + { 578, PT_SC, ucp_Lao }, + { 582, PT_SC, ucp_Latin }, + { 588, PT_SC, ucp_Lepcha }, + { 595, PT_SC, ucp_Limbu }, + { 601, PT_SC, ucp_Linear_A }, + { 610, PT_SC, ucp_Linear_B }, + { 619, PT_SC, ucp_Lisu }, + { 624, PT_PC, ucp_Ll }, + { 627, PT_PC, ucp_Lm }, + { 630, PT_PC, ucp_Lo }, + { 633, PT_PC, ucp_Lt }, + { 636, PT_PC, ucp_Lu }, + { 639, PT_SC, ucp_Lycian }, + { 646, PT_SC, ucp_Lydian }, + { 653, PT_GC, ucp_M }, + { 655, PT_SC, ucp_Mahajani }, + { 664, PT_SC, ucp_Malayalam }, + { 674, PT_SC, ucp_Mandaic }, + { 682, PT_SC, ucp_Manichaean }, + { 693, PT_PC, ucp_Mc }, + { 696, PT_PC, ucp_Me }, + { 699, PT_SC, ucp_Meetei_Mayek }, + { 712, PT_SC, ucp_Mende_Kikakui }, + { 726, PT_SC, ucp_Meroitic_Cursive }, + { 743, PT_SC, ucp_Meroitic_Hieroglyphs }, + { 764, PT_SC, ucp_Miao }, + { 769, PT_PC, ucp_Mn }, + { 772, PT_SC, ucp_Modi }, + { 777, PT_SC, ucp_Mongolian }, + { 787, PT_SC, ucp_Mro }, + { 791, PT_SC, ucp_Multani }, + { 799, PT_SC, ucp_Myanmar }, + { 807, PT_GC, ucp_N }, + { 809, PT_SC, ucp_Nabataean }, + { 819, PT_PC, ucp_Nd }, + { 822, PT_SC, ucp_New_Tai_Lue }, + { 834, PT_SC, ucp_Nko }, + { 838, PT_PC, ucp_Nl }, + { 841, PT_PC, ucp_No }, + { 844, PT_SC, ucp_Ogham }, + { 850, PT_SC, ucp_Ol_Chiki }, + { 859, PT_SC, ucp_Old_Hungarian }, + { 873, PT_SC, ucp_Old_Italic }, + { 884, PT_SC, ucp_Old_North_Arabian }, + { 902, PT_SC, ucp_Old_Permic }, + { 913, PT_SC, ucp_Old_Persian }, + { 925, PT_SC, ucp_Old_South_Arabian }, + { 943, PT_SC, ucp_Old_Turkic }, + { 954, PT_SC, ucp_Oriya }, + { 960, PT_SC, ucp_Osmanya }, + { 968, PT_GC, ucp_P }, + { 970, PT_SC, ucp_Pahawh_Hmong }, + { 983, PT_SC, ucp_Palmyrene }, + { 993, PT_SC, ucp_Pau_Cin_Hau }, + { 1005, PT_PC, ucp_Pc }, + { 1008, PT_PC, ucp_Pd }, + { 1011, PT_PC, ucp_Pe }, + { 1014, PT_PC, ucp_Pf }, + { 1017, PT_SC, ucp_Phags_Pa }, + { 1026, PT_SC, ucp_Phoenician }, + { 1037, PT_PC, ucp_Pi }, + { 1040, PT_PC, ucp_Po }, + { 1043, PT_PC, ucp_Ps }, + { 1046, PT_SC, ucp_Psalter_Pahlavi }, + { 1062, PT_SC, ucp_Rejang }, + { 1069, PT_SC, ucp_Runic }, + { 1075, PT_GC, ucp_S }, + { 1077, PT_SC, ucp_Samaritan }, + { 1087, PT_SC, ucp_Saurashtra }, + { 1098, PT_PC, ucp_Sc }, + { 1101, PT_SC, ucp_Sharada }, + { 1109, PT_SC, ucp_Shavian }, + { 1117, PT_SC, ucp_Siddham }, + { 1125, PT_SC, ucp_SignWriting }, + { 1137, PT_SC, ucp_Sinhala }, + { 1145, PT_PC, ucp_Sk }, + { 1148, PT_PC, ucp_Sm }, + { 1151, PT_PC, ucp_So }, + { 1154, PT_SC, ucp_Sora_Sompeng }, + { 1167, PT_SC, ucp_Sundanese }, + { 1177, PT_SC, ucp_Syloti_Nagri }, + { 1190, PT_SC, ucp_Syriac }, + { 1197, PT_SC, ucp_Tagalog }, + { 1205, PT_SC, ucp_Tagbanwa }, + { 1214, PT_SC, ucp_Tai_Le }, + { 1221, PT_SC, ucp_Tai_Tham }, + { 1230, PT_SC, ucp_Tai_Viet }, + { 1239, PT_SC, ucp_Takri }, + { 1245, PT_SC, ucp_Tamil }, + { 1251, PT_SC, ucp_Telugu }, + { 1258, PT_SC, ucp_Thaana }, + { 1265, PT_SC, ucp_Thai }, + { 1270, PT_SC, ucp_Tibetan }, + { 1278, PT_SC, ucp_Tifinagh }, + { 1287, PT_SC, ucp_Tirhuta }, + { 1295, PT_SC, ucp_Ugaritic }, + { 1304, PT_SC, ucp_Vai }, + { 1308, PT_SC, ucp_Warang_Citi }, + { 1320, PT_ALNUM, 0 }, + { 1324, PT_PXSPACE, 0 }, + { 1328, PT_SPACE, 0 }, + { 1332, PT_UCNC, 0 }, + { 1336, PT_WORD, 0 }, + { 1340, PT_SC, ucp_Yi }, + { 1343, PT_GC, ucp_Z }, + { 1345, PT_PC, ucp_Zl }, + { 1348, PT_PC, ucp_Zp }, + { 1351, PT_PC, ucp_Zs } }; const size_t PRIV(utt_size) = sizeof(PRIV(utt)) / sizeof(ucp_type_table); diff --git a/pcre2-10.20/src/pcre2_ucd.c b/pcre2-10.21/src/pcre2_ucd.c similarity index 51% rename from pcre2-10.20/src/pcre2_ucd.c rename to pcre2-10.21/src/pcre2_ucd.c index 7199cbda7..116f537b3 100644 --- a/pcre2-10.20/src/pcre2_ucd.c +++ b/pcre2-10.21/src/pcre2_ucd.c @@ -20,7 +20,7 @@ needed. */ /* Unicode character database. */ /* This file was autogenerated by the MultiStage2.py script. */ -/* Total size: 72576 bytes, block size: 128. */ +/* Total size: 75072 bytes, block size: 128. */ /* The tables herein are needed only when UCP support is built, and in PCRE2 that happens automatically with UTF support. @@ -39,7 +39,7 @@ const uint16_t PRIV(ucd_stage2)[] = {0}; const uint32_t PRIV(ucd_caseless_sets)[] = {0}; #else -const char *PRIV(unicode_version) = "7.0.0"; +const char *PRIV(unicode_version) = "8.0.0"; /* When recompiling tables with a new Unicode version, please check the types in this structure definition from pcre2_internal.h (the actual @@ -82,7 +82,7 @@ const uint32_t PRIV(ucd_caseless_sets)[] = { #ifndef PCRE2_PCRE2TEST -const ucd_record PRIV(ucd_records)[] = { /* 5760 bytes, record size 8 */ +const ucd_record PRIV(ucd_records)[] = { /* 5952 bytes, record size 8 */ { 9, 0, 2, 0, 0, }, /* 0 */ { 9, 0, 1, 0, 0, }, /* 1 */ { 9, 0, 0, 0, 0, }, /* 2 */ @@ -188,621 +188,645 @@ const ucd_record PRIV(ucd_records)[] = { /* 5760 bytes, record size 8 */ { 33, 5, 12, 0, -217, }, /* 102 */ { 33, 5, 12, 0, -71, }, /* 103 */ { 33, 5, 12, 0, -219, }, /* 104 */ - { 33, 5, 12, 0, 42258, }, /* 105 */ - { 33, 6, 12, 0, 0, }, /* 106 */ - { 9, 6, 12, 0, 0, }, /* 107 */ - { 3, 24, 12, 0, 0, }, /* 108 */ - { 27, 12, 3, 0, 0, }, /* 109 */ - { 27, 12, 3, 21, 116, }, /* 110 */ - { 19, 9, 12, 0, 1, }, /* 111 */ - { 19, 5, 12, 0, -1, }, /* 112 */ - { 19, 24, 12, 0, 0, }, /* 113 */ - { 9, 2, 12, 0, 0, }, /* 114 */ - { 19, 6, 12, 0, 0, }, /* 115 */ - { 19, 5, 12, 0, 130, }, /* 116 */ - { 19, 9, 12, 0, 116, }, /* 117 */ - { 19, 9, 12, 0, 38, }, /* 118 */ - { 19, 9, 12, 0, 37, }, /* 119 */ - { 19, 9, 12, 0, 64, }, /* 120 */ - { 19, 9, 12, 0, 63, }, /* 121 */ - { 19, 5, 12, 0, 0, }, /* 122 */ - { 19, 9, 12, 0, 32, }, /* 123 */ - { 19, 9, 12, 34, 32, }, /* 124 */ - { 19, 9, 12, 59, 32, }, /* 125 */ - { 19, 9, 12, 38, 32, }, /* 126 */ - { 19, 9, 12, 21, 32, }, /* 127 */ - { 19, 9, 12, 51, 32, }, /* 128 */ - { 19, 9, 12, 26, 32, }, /* 129 */ - { 19, 9, 12, 47, 32, }, /* 130 */ - { 19, 9, 12, 55, 32, }, /* 131 */ - { 19, 9, 12, 30, 32, }, /* 132 */ - { 19, 9, 12, 43, 32, }, /* 133 */ - { 19, 9, 12, 67, 32, }, /* 134 */ - { 19, 5, 12, 0, -38, }, /* 135 */ - { 19, 5, 12, 0, -37, }, /* 136 */ - { 19, 5, 12, 0, -32, }, /* 137 */ - { 19, 5, 12, 34, -32, }, /* 138 */ - { 19, 5, 12, 59, -32, }, /* 139 */ - { 19, 5, 12, 38, -32, }, /* 140 */ - { 19, 5, 12, 21, -116, }, /* 141 */ - { 19, 5, 12, 51, -32, }, /* 142 */ - { 19, 5, 12, 26, -775, }, /* 143 */ - { 19, 5, 12, 47, -32, }, /* 144 */ - { 19, 5, 12, 55, -32, }, /* 145 */ - { 19, 5, 12, 30, 1, }, /* 146 */ - { 19, 5, 12, 30, -32, }, /* 147 */ - { 19, 5, 12, 43, -32, }, /* 148 */ - { 19, 5, 12, 67, -32, }, /* 149 */ - { 19, 5, 12, 0, -64, }, /* 150 */ - { 19, 5, 12, 0, -63, }, /* 151 */ - { 19, 9, 12, 0, 8, }, /* 152 */ - { 19, 5, 12, 34, -30, }, /* 153 */ - { 19, 5, 12, 38, -25, }, /* 154 */ - { 19, 9, 12, 0, 0, }, /* 155 */ - { 19, 5, 12, 43, -15, }, /* 156 */ - { 19, 5, 12, 47, -22, }, /* 157 */ - { 19, 5, 12, 0, -8, }, /* 158 */ - { 10, 9, 12, 0, 1, }, /* 159 */ - { 10, 5, 12, 0, -1, }, /* 160 */ - { 19, 5, 12, 51, -54, }, /* 161 */ - { 19, 5, 12, 55, -48, }, /* 162 */ - { 19, 5, 12, 0, 7, }, /* 163 */ - { 19, 5, 12, 0, -116, }, /* 164 */ - { 19, 9, 12, 38, -60, }, /* 165 */ - { 19, 5, 12, 59, -64, }, /* 166 */ - { 19, 25, 12, 0, 0, }, /* 167 */ - { 19, 9, 12, 0, -7, }, /* 168 */ - { 19, 9, 12, 0, -130, }, /* 169 */ - { 12, 9, 12, 0, 80, }, /* 170 */ - { 12, 9, 12, 0, 32, }, /* 171 */ - { 12, 5, 12, 0, -32, }, /* 172 */ - { 12, 5, 12, 0, -80, }, /* 173 */ - { 12, 9, 12, 0, 1, }, /* 174 */ - { 12, 5, 12, 0, -1, }, /* 175 */ - { 12, 26, 12, 0, 0, }, /* 176 */ - { 12, 12, 3, 0, 0, }, /* 177 */ - { 12, 11, 3, 0, 0, }, /* 178 */ - { 12, 9, 12, 0, 15, }, /* 179 */ - { 12, 5, 12, 0, -15, }, /* 180 */ - { 1, 9, 12, 0, 48, }, /* 181 */ - { 1, 6, 12, 0, 0, }, /* 182 */ - { 1, 21, 12, 0, 0, }, /* 183 */ - { 1, 5, 12, 0, -48, }, /* 184 */ - { 1, 5, 12, 0, 0, }, /* 185 */ - { 1, 17, 12, 0, 0, }, /* 186 */ - { 1, 26, 12, 0, 0, }, /* 187 */ - { 1, 23, 12, 0, 0, }, /* 188 */ - { 25, 12, 3, 0, 0, }, /* 189 */ - { 25, 17, 12, 0, 0, }, /* 190 */ - { 25, 21, 12, 0, 0, }, /* 191 */ - { 25, 7, 12, 0, 0, }, /* 192 */ - { 0, 1, 2, 0, 0, }, /* 193 */ - { 0, 25, 12, 0, 0, }, /* 194 */ - { 0, 21, 12, 0, 0, }, /* 195 */ - { 0, 23, 12, 0, 0, }, /* 196 */ - { 0, 26, 12, 0, 0, }, /* 197 */ - { 0, 12, 3, 0, 0, }, /* 198 */ - { 0, 7, 12, 0, 0, }, /* 199 */ - { 0, 6, 12, 0, 0, }, /* 200 */ + { 33, 5, 12, 0, 42261, }, /* 105 */ + { 33, 5, 12, 0, 42258, }, /* 106 */ + { 33, 6, 12, 0, 0, }, /* 107 */ + { 9, 6, 12, 0, 0, }, /* 108 */ + { 3, 24, 12, 0, 0, }, /* 109 */ + { 27, 12, 3, 0, 0, }, /* 110 */ + { 27, 12, 3, 21, 116, }, /* 111 */ + { 19, 9, 12, 0, 1, }, /* 112 */ + { 19, 5, 12, 0, -1, }, /* 113 */ + { 19, 24, 12, 0, 0, }, /* 114 */ + { 9, 2, 12, 0, 0, }, /* 115 */ + { 19, 6, 12, 0, 0, }, /* 116 */ + { 19, 5, 12, 0, 130, }, /* 117 */ + { 19, 9, 12, 0, 116, }, /* 118 */ + { 19, 9, 12, 0, 38, }, /* 119 */ + { 19, 9, 12, 0, 37, }, /* 120 */ + { 19, 9, 12, 0, 64, }, /* 121 */ + { 19, 9, 12, 0, 63, }, /* 122 */ + { 19, 5, 12, 0, 0, }, /* 123 */ + { 19, 9, 12, 0, 32, }, /* 124 */ + { 19, 9, 12, 34, 32, }, /* 125 */ + { 19, 9, 12, 59, 32, }, /* 126 */ + { 19, 9, 12, 38, 32, }, /* 127 */ + { 19, 9, 12, 21, 32, }, /* 128 */ + { 19, 9, 12, 51, 32, }, /* 129 */ + { 19, 9, 12, 26, 32, }, /* 130 */ + { 19, 9, 12, 47, 32, }, /* 131 */ + { 19, 9, 12, 55, 32, }, /* 132 */ + { 19, 9, 12, 30, 32, }, /* 133 */ + { 19, 9, 12, 43, 32, }, /* 134 */ + { 19, 9, 12, 67, 32, }, /* 135 */ + { 19, 5, 12, 0, -38, }, /* 136 */ + { 19, 5, 12, 0, -37, }, /* 137 */ + { 19, 5, 12, 0, -32, }, /* 138 */ + { 19, 5, 12, 34, -32, }, /* 139 */ + { 19, 5, 12, 59, -32, }, /* 140 */ + { 19, 5, 12, 38, -32, }, /* 141 */ + { 19, 5, 12, 21, -116, }, /* 142 */ + { 19, 5, 12, 51, -32, }, /* 143 */ + { 19, 5, 12, 26, -775, }, /* 144 */ + { 19, 5, 12, 47, -32, }, /* 145 */ + { 19, 5, 12, 55, -32, }, /* 146 */ + { 19, 5, 12, 30, 1, }, /* 147 */ + { 19, 5, 12, 30, -32, }, /* 148 */ + { 19, 5, 12, 43, -32, }, /* 149 */ + { 19, 5, 12, 67, -32, }, /* 150 */ + { 19, 5, 12, 0, -64, }, /* 151 */ + { 19, 5, 12, 0, -63, }, /* 152 */ + { 19, 9, 12, 0, 8, }, /* 153 */ + { 19, 5, 12, 34, -30, }, /* 154 */ + { 19, 5, 12, 38, -25, }, /* 155 */ + { 19, 9, 12, 0, 0, }, /* 156 */ + { 19, 5, 12, 43, -15, }, /* 157 */ + { 19, 5, 12, 47, -22, }, /* 158 */ + { 19, 5, 12, 0, -8, }, /* 159 */ + { 10, 9, 12, 0, 1, }, /* 160 */ + { 10, 5, 12, 0, -1, }, /* 161 */ + { 19, 5, 12, 51, -54, }, /* 162 */ + { 19, 5, 12, 55, -48, }, /* 163 */ + { 19, 5, 12, 0, 7, }, /* 164 */ + { 19, 5, 12, 0, -116, }, /* 165 */ + { 19, 9, 12, 38, -60, }, /* 166 */ + { 19, 5, 12, 59, -64, }, /* 167 */ + { 19, 25, 12, 0, 0, }, /* 168 */ + { 19, 9, 12, 0, -7, }, /* 169 */ + { 19, 9, 12, 0, -130, }, /* 170 */ + { 12, 9, 12, 0, 80, }, /* 171 */ + { 12, 9, 12, 0, 32, }, /* 172 */ + { 12, 5, 12, 0, -32, }, /* 173 */ + { 12, 5, 12, 0, -80, }, /* 174 */ + { 12, 9, 12, 0, 1, }, /* 175 */ + { 12, 5, 12, 0, -1, }, /* 176 */ + { 12, 26, 12, 0, 0, }, /* 177 */ + { 12, 12, 3, 0, 0, }, /* 178 */ + { 12, 11, 3, 0, 0, }, /* 179 */ + { 12, 9, 12, 0, 15, }, /* 180 */ + { 12, 5, 12, 0, -15, }, /* 181 */ + { 1, 9, 12, 0, 48, }, /* 182 */ + { 1, 6, 12, 0, 0, }, /* 183 */ + { 1, 21, 12, 0, 0, }, /* 184 */ + { 1, 5, 12, 0, -48, }, /* 185 */ + { 1, 5, 12, 0, 0, }, /* 186 */ + { 1, 17, 12, 0, 0, }, /* 187 */ + { 1, 26, 12, 0, 0, }, /* 188 */ + { 1, 23, 12, 0, 0, }, /* 189 */ + { 25, 12, 3, 0, 0, }, /* 190 */ + { 25, 17, 12, 0, 0, }, /* 191 */ + { 25, 21, 12, 0, 0, }, /* 192 */ + { 25, 7, 12, 0, 0, }, /* 193 */ + { 0, 1, 2, 0, 0, }, /* 194 */ + { 0, 25, 12, 0, 0, }, /* 195 */ + { 0, 21, 12, 0, 0, }, /* 196 */ + { 0, 23, 12, 0, 0, }, /* 197 */ + { 0, 26, 12, 0, 0, }, /* 198 */ + { 0, 12, 3, 0, 0, }, /* 199 */ + { 0, 7, 12, 0, 0, }, /* 200 */ { 0, 13, 12, 0, 0, }, /* 201 */ - { 49, 21, 12, 0, 0, }, /* 202 */ - { 49, 1, 2, 0, 0, }, /* 203 */ - { 49, 7, 12, 0, 0, }, /* 204 */ - { 49, 12, 3, 0, 0, }, /* 205 */ - { 55, 7, 12, 0, 0, }, /* 206 */ - { 55, 12, 3, 0, 0, }, /* 207 */ - { 63, 13, 12, 0, 0, }, /* 208 */ - { 63, 7, 12, 0, 0, }, /* 209 */ - { 63, 12, 3, 0, 0, }, /* 210 */ - { 63, 6, 12, 0, 0, }, /* 211 */ - { 63, 26, 12, 0, 0, }, /* 212 */ - { 63, 21, 12, 0, 0, }, /* 213 */ - { 89, 7, 12, 0, 0, }, /* 214 */ - { 89, 12, 3, 0, 0, }, /* 215 */ - { 89, 6, 12, 0, 0, }, /* 216 */ - { 89, 21, 12, 0, 0, }, /* 217 */ - { 94, 7, 12, 0, 0, }, /* 218 */ - { 94, 12, 3, 0, 0, }, /* 219 */ - { 94, 21, 12, 0, 0, }, /* 220 */ - { 14, 12, 3, 0, 0, }, /* 221 */ - { 14, 10, 5, 0, 0, }, /* 222 */ - { 14, 7, 12, 0, 0, }, /* 223 */ - { 14, 13, 12, 0, 0, }, /* 224 */ - { 14, 21, 12, 0, 0, }, /* 225 */ - { 14, 6, 12, 0, 0, }, /* 226 */ - { 2, 7, 12, 0, 0, }, /* 227 */ - { 2, 12, 3, 0, 0, }, /* 228 */ - { 2, 10, 5, 0, 0, }, /* 229 */ - { 2, 10, 3, 0, 0, }, /* 230 */ - { 2, 13, 12, 0, 0, }, /* 231 */ - { 2, 23, 12, 0, 0, }, /* 232 */ - { 2, 15, 12, 0, 0, }, /* 233 */ - { 2, 26, 12, 0, 0, }, /* 234 */ - { 21, 12, 3, 0, 0, }, /* 235 */ - { 21, 10, 5, 0, 0, }, /* 236 */ - { 21, 7, 12, 0, 0, }, /* 237 */ - { 21, 13, 12, 0, 0, }, /* 238 */ - { 20, 12, 3, 0, 0, }, /* 239 */ - { 20, 10, 5, 0, 0, }, /* 240 */ - { 20, 7, 12, 0, 0, }, /* 241 */ - { 20, 13, 12, 0, 0, }, /* 242 */ - { 20, 21, 12, 0, 0, }, /* 243 */ - { 20, 23, 12, 0, 0, }, /* 244 */ - { 43, 12, 3, 0, 0, }, /* 245 */ - { 43, 10, 5, 0, 0, }, /* 246 */ - { 43, 7, 12, 0, 0, }, /* 247 */ - { 43, 10, 3, 0, 0, }, /* 248 */ - { 43, 13, 12, 0, 0, }, /* 249 */ - { 43, 26, 12, 0, 0, }, /* 250 */ - { 43, 15, 12, 0, 0, }, /* 251 */ - { 53, 12, 3, 0, 0, }, /* 252 */ - { 53, 7, 12, 0, 0, }, /* 253 */ - { 53, 10, 3, 0, 0, }, /* 254 */ - { 53, 10, 5, 0, 0, }, /* 255 */ - { 53, 13, 12, 0, 0, }, /* 256 */ - { 53, 15, 12, 0, 0, }, /* 257 */ - { 53, 26, 12, 0, 0, }, /* 258 */ - { 53, 23, 12, 0, 0, }, /* 259 */ - { 54, 12, 3, 0, 0, }, /* 260 */ - { 54, 10, 5, 0, 0, }, /* 261 */ - { 54, 7, 12, 0, 0, }, /* 262 */ - { 54, 13, 12, 0, 0, }, /* 263 */ - { 54, 15, 12, 0, 0, }, /* 264 */ - { 54, 26, 12, 0, 0, }, /* 265 */ - { 28, 12, 3, 0, 0, }, /* 266 */ - { 28, 10, 5, 0, 0, }, /* 267 */ - { 28, 7, 12, 0, 0, }, /* 268 */ - { 28, 10, 3, 0, 0, }, /* 269 */ - { 28, 13, 12, 0, 0, }, /* 270 */ - { 36, 12, 3, 0, 0, }, /* 271 */ - { 36, 10, 5, 0, 0, }, /* 272 */ - { 36, 7, 12, 0, 0, }, /* 273 */ - { 36, 10, 3, 0, 0, }, /* 274 */ - { 36, 13, 12, 0, 0, }, /* 275 */ - { 36, 15, 12, 0, 0, }, /* 276 */ - { 36, 26, 12, 0, 0, }, /* 277 */ - { 47, 10, 5, 0, 0, }, /* 278 */ - { 47, 7, 12, 0, 0, }, /* 279 */ - { 47, 12, 3, 0, 0, }, /* 280 */ - { 47, 10, 3, 0, 0, }, /* 281 */ - { 47, 13, 12, 0, 0, }, /* 282 */ - { 47, 21, 12, 0, 0, }, /* 283 */ - { 56, 7, 12, 0, 0, }, /* 284 */ - { 56, 12, 3, 0, 0, }, /* 285 */ - { 56, 7, 5, 0, 0, }, /* 286 */ - { 56, 6, 12, 0, 0, }, /* 287 */ - { 56, 21, 12, 0, 0, }, /* 288 */ - { 56, 13, 12, 0, 0, }, /* 289 */ - { 32, 7, 12, 0, 0, }, /* 290 */ - { 32, 12, 3, 0, 0, }, /* 291 */ - { 32, 7, 5, 0, 0, }, /* 292 */ - { 32, 6, 12, 0, 0, }, /* 293 */ - { 32, 13, 12, 0, 0, }, /* 294 */ - { 57, 7, 12, 0, 0, }, /* 295 */ - { 57, 26, 12, 0, 0, }, /* 296 */ - { 57, 21, 12, 0, 0, }, /* 297 */ - { 57, 12, 3, 0, 0, }, /* 298 */ - { 57, 13, 12, 0, 0, }, /* 299 */ - { 57, 15, 12, 0, 0, }, /* 300 */ - { 57, 22, 12, 0, 0, }, /* 301 */ - { 57, 18, 12, 0, 0, }, /* 302 */ - { 57, 10, 5, 0, 0, }, /* 303 */ - { 38, 7, 12, 0, 0, }, /* 304 */ - { 38, 10, 12, 0, 0, }, /* 305 */ - { 38, 12, 3, 0, 0, }, /* 306 */ - { 38, 10, 5, 0, 0, }, /* 307 */ - { 38, 13, 12, 0, 0, }, /* 308 */ - { 38, 21, 12, 0, 0, }, /* 309 */ - { 38, 26, 12, 0, 0, }, /* 310 */ - { 16, 9, 12, 0, 7264, }, /* 311 */ - { 16, 7, 12, 0, 0, }, /* 312 */ - { 16, 6, 12, 0, 0, }, /* 313 */ - { 23, 7, 6, 0, 0, }, /* 314 */ - { 23, 7, 7, 0, 0, }, /* 315 */ - { 23, 7, 8, 0, 0, }, /* 316 */ - { 15, 7, 12, 0, 0, }, /* 317 */ - { 15, 12, 3, 0, 0, }, /* 318 */ - { 15, 21, 12, 0, 0, }, /* 319 */ - { 15, 15, 12, 0, 0, }, /* 320 */ - { 15, 26, 12, 0, 0, }, /* 321 */ - { 8, 7, 12, 0, 0, }, /* 322 */ - { 7, 17, 12, 0, 0, }, /* 323 */ - { 7, 7, 12, 0, 0, }, /* 324 */ - { 7, 21, 12, 0, 0, }, /* 325 */ - { 40, 29, 12, 0, 0, }, /* 326 */ - { 40, 7, 12, 0, 0, }, /* 327 */ - { 40, 22, 12, 0, 0, }, /* 328 */ - { 40, 18, 12, 0, 0, }, /* 329 */ - { 45, 7, 12, 0, 0, }, /* 330 */ - { 45, 14, 12, 0, 0, }, /* 331 */ - { 50, 7, 12, 0, 0, }, /* 332 */ - { 50, 12, 3, 0, 0, }, /* 333 */ - { 24, 7, 12, 0, 0, }, /* 334 */ - { 24, 12, 3, 0, 0, }, /* 335 */ - { 6, 7, 12, 0, 0, }, /* 336 */ - { 6, 12, 3, 0, 0, }, /* 337 */ - { 51, 7, 12, 0, 0, }, /* 338 */ - { 51, 12, 3, 0, 0, }, /* 339 */ - { 31, 7, 12, 0, 0, }, /* 340 */ - { 31, 12, 3, 0, 0, }, /* 341 */ - { 31, 10, 5, 0, 0, }, /* 342 */ - { 31, 21, 12, 0, 0, }, /* 343 */ - { 31, 6, 12, 0, 0, }, /* 344 */ - { 31, 23, 12, 0, 0, }, /* 345 */ - { 31, 13, 12, 0, 0, }, /* 346 */ - { 31, 15, 12, 0, 0, }, /* 347 */ - { 37, 21, 12, 0, 0, }, /* 348 */ - { 37, 17, 12, 0, 0, }, /* 349 */ - { 37, 12, 3, 0, 0, }, /* 350 */ - { 37, 1, 2, 0, 0, }, /* 351 */ - { 37, 13, 12, 0, 0, }, /* 352 */ - { 37, 7, 12, 0, 0, }, /* 353 */ - { 37, 6, 12, 0, 0, }, /* 354 */ - { 34, 7, 12, 0, 0, }, /* 355 */ - { 34, 12, 3, 0, 0, }, /* 356 */ - { 34, 10, 5, 0, 0, }, /* 357 */ - { 34, 26, 12, 0, 0, }, /* 358 */ - { 34, 21, 12, 0, 0, }, /* 359 */ - { 34, 13, 12, 0, 0, }, /* 360 */ - { 52, 7, 12, 0, 0, }, /* 361 */ - { 39, 7, 12, 0, 0, }, /* 362 */ - { 39, 10, 12, 0, 0, }, /* 363 */ - { 39, 10, 5, 0, 0, }, /* 364 */ - { 39, 13, 12, 0, 0, }, /* 365 */ - { 39, 15, 12, 0, 0, }, /* 366 */ - { 39, 26, 12, 0, 0, }, /* 367 */ - { 31, 26, 12, 0, 0, }, /* 368 */ - { 5, 7, 12, 0, 0, }, /* 369 */ - { 5, 12, 3, 0, 0, }, /* 370 */ - { 5, 10, 5, 0, 0, }, /* 371 */ - { 5, 21, 12, 0, 0, }, /* 372 */ - { 90, 7, 12, 0, 0, }, /* 373 */ - { 90, 10, 5, 0, 0, }, /* 374 */ - { 90, 12, 3, 0, 0, }, /* 375 */ - { 90, 10, 12, 0, 0, }, /* 376 */ - { 90, 13, 12, 0, 0, }, /* 377 */ - { 90, 21, 12, 0, 0, }, /* 378 */ - { 90, 6, 12, 0, 0, }, /* 379 */ - { 27, 11, 3, 0, 0, }, /* 380 */ - { 61, 12, 3, 0, 0, }, /* 381 */ - { 61, 10, 5, 0, 0, }, /* 382 */ - { 61, 7, 12, 0, 0, }, /* 383 */ - { 61, 13, 12, 0, 0, }, /* 384 */ - { 61, 21, 12, 0, 0, }, /* 385 */ - { 61, 26, 12, 0, 0, }, /* 386 */ - { 75, 12, 3, 0, 0, }, /* 387 */ - { 75, 10, 5, 0, 0, }, /* 388 */ - { 75, 7, 12, 0, 0, }, /* 389 */ - { 75, 13, 12, 0, 0, }, /* 390 */ - { 92, 7, 12, 0, 0, }, /* 391 */ - { 92, 12, 3, 0, 0, }, /* 392 */ - { 92, 10, 5, 0, 0, }, /* 393 */ - { 92, 21, 12, 0, 0, }, /* 394 */ - { 69, 7, 12, 0, 0, }, /* 395 */ - { 69, 10, 5, 0, 0, }, /* 396 */ - { 69, 12, 3, 0, 0, }, /* 397 */ - { 69, 21, 12, 0, 0, }, /* 398 */ - { 69, 13, 12, 0, 0, }, /* 399 */ - { 72, 13, 12, 0, 0, }, /* 400 */ - { 72, 7, 12, 0, 0, }, /* 401 */ - { 72, 6, 12, 0, 0, }, /* 402 */ - { 72, 21, 12, 0, 0, }, /* 403 */ - { 75, 21, 12, 0, 0, }, /* 404 */ - { 9, 10, 5, 0, 0, }, /* 405 */ - { 9, 7, 12, 0, 0, }, /* 406 */ - { 12, 5, 12, 0, 0, }, /* 407 */ - { 12, 6, 12, 0, 0, }, /* 408 */ - { 33, 5, 12, 0, 35332, }, /* 409 */ - { 33, 5, 12, 0, 3814, }, /* 410 */ - { 33, 9, 12, 63, 1, }, /* 411 */ - { 33, 5, 12, 63, -1, }, /* 412 */ - { 33, 5, 12, 63, -58, }, /* 413 */ - { 33, 9, 12, 0, -7615, }, /* 414 */ - { 19, 5, 12, 0, 8, }, /* 415 */ - { 19, 9, 12, 0, -8, }, /* 416 */ - { 19, 5, 12, 0, 74, }, /* 417 */ - { 19, 5, 12, 0, 86, }, /* 418 */ - { 19, 5, 12, 0, 100, }, /* 419 */ - { 19, 5, 12, 0, 128, }, /* 420 */ - { 19, 5, 12, 0, 112, }, /* 421 */ - { 19, 5, 12, 0, 126, }, /* 422 */ - { 19, 8, 12, 0, -8, }, /* 423 */ - { 19, 5, 12, 0, 9, }, /* 424 */ - { 19, 9, 12, 0, -74, }, /* 425 */ - { 19, 8, 12, 0, -9, }, /* 426 */ - { 19, 5, 12, 21, -7173, }, /* 427 */ - { 19, 9, 12, 0, -86, }, /* 428 */ - { 19, 9, 12, 0, -100, }, /* 429 */ - { 19, 9, 12, 0, -112, }, /* 430 */ - { 19, 9, 12, 0, -128, }, /* 431 */ - { 19, 9, 12, 0, -126, }, /* 432 */ - { 27, 1, 3, 0, 0, }, /* 433 */ - { 9, 27, 2, 0, 0, }, /* 434 */ - { 9, 28, 2, 0, 0, }, /* 435 */ - { 9, 2, 2, 0, 0, }, /* 436 */ - { 9, 9, 12, 0, 0, }, /* 437 */ - { 9, 5, 12, 0, 0, }, /* 438 */ - { 19, 9, 12, 67, -7517, }, /* 439 */ - { 33, 9, 12, 71, -8383, }, /* 440 */ - { 33, 9, 12, 75, -8262, }, /* 441 */ - { 33, 9, 12, 0, 28, }, /* 442 */ - { 33, 5, 12, 0, -28, }, /* 443 */ - { 33, 14, 12, 0, 16, }, /* 444 */ - { 33, 14, 12, 0, -16, }, /* 445 */ - { 33, 14, 12, 0, 0, }, /* 446 */ - { 9, 26, 12, 0, 26, }, /* 447 */ - { 9, 26, 12, 0, -26, }, /* 448 */ - { 4, 26, 12, 0, 0, }, /* 449 */ - { 17, 9, 12, 0, 48, }, /* 450 */ - { 17, 5, 12, 0, -48, }, /* 451 */ - { 33, 9, 12, 0, -10743, }, /* 452 */ - { 33, 9, 12, 0, -3814, }, /* 453 */ - { 33, 9, 12, 0, -10727, }, /* 454 */ - { 33, 5, 12, 0, -10795, }, /* 455 */ - { 33, 5, 12, 0, -10792, }, /* 456 */ - { 33, 9, 12, 0, -10780, }, /* 457 */ - { 33, 9, 12, 0, -10749, }, /* 458 */ - { 33, 9, 12, 0, -10783, }, /* 459 */ - { 33, 9, 12, 0, -10782, }, /* 460 */ - { 33, 9, 12, 0, -10815, }, /* 461 */ - { 10, 5, 12, 0, 0, }, /* 462 */ - { 10, 26, 12, 0, 0, }, /* 463 */ - { 10, 12, 3, 0, 0, }, /* 464 */ - { 10, 21, 12, 0, 0, }, /* 465 */ - { 10, 15, 12, 0, 0, }, /* 466 */ - { 16, 5, 12, 0, -7264, }, /* 467 */ - { 58, 7, 12, 0, 0, }, /* 468 */ - { 58, 6, 12, 0, 0, }, /* 469 */ - { 58, 21, 12, 0, 0, }, /* 470 */ - { 58, 12, 3, 0, 0, }, /* 471 */ - { 22, 26, 12, 0, 0, }, /* 472 */ - { 22, 6, 12, 0, 0, }, /* 473 */ - { 22, 14, 12, 0, 0, }, /* 474 */ - { 23, 10, 3, 0, 0, }, /* 475 */ - { 26, 7, 12, 0, 0, }, /* 476 */ - { 26, 6, 12, 0, 0, }, /* 477 */ - { 29, 7, 12, 0, 0, }, /* 478 */ - { 29, 6, 12, 0, 0, }, /* 479 */ - { 3, 7, 12, 0, 0, }, /* 480 */ - { 23, 7, 12, 0, 0, }, /* 481 */ - { 23, 26, 12, 0, 0, }, /* 482 */ - { 29, 26, 12, 0, 0, }, /* 483 */ - { 22, 7, 12, 0, 0, }, /* 484 */ - { 60, 7, 12, 0, 0, }, /* 485 */ - { 60, 6, 12, 0, 0, }, /* 486 */ - { 60, 26, 12, 0, 0, }, /* 487 */ - { 85, 7, 12, 0, 0, }, /* 488 */ - { 85, 6, 12, 0, 0, }, /* 489 */ - { 85, 21, 12, 0, 0, }, /* 490 */ - { 76, 7, 12, 0, 0, }, /* 491 */ - { 76, 6, 12, 0, 0, }, /* 492 */ - { 76, 21, 12, 0, 0, }, /* 493 */ - { 76, 13, 12, 0, 0, }, /* 494 */ - { 12, 7, 12, 0, 0, }, /* 495 */ - { 12, 21, 12, 0, 0, }, /* 496 */ - { 78, 7, 12, 0, 0, }, /* 497 */ - { 78, 14, 12, 0, 0, }, /* 498 */ - { 78, 12, 3, 0, 0, }, /* 499 */ - { 78, 21, 12, 0, 0, }, /* 500 */ - { 33, 9, 12, 0, -35332, }, /* 501 */ - { 33, 9, 12, 0, -42280, }, /* 502 */ - { 33, 9, 12, 0, -42308, }, /* 503 */ - { 33, 9, 12, 0, -42319, }, /* 504 */ - { 33, 9, 12, 0, -42315, }, /* 505 */ - { 33, 9, 12, 0, -42305, }, /* 506 */ - { 33, 9, 12, 0, -42258, }, /* 507 */ - { 33, 9, 12, 0, -42282, }, /* 508 */ - { 48, 7, 12, 0, 0, }, /* 509 */ - { 48, 12, 3, 0, 0, }, /* 510 */ - { 48, 10, 5, 0, 0, }, /* 511 */ - { 48, 26, 12, 0, 0, }, /* 512 */ - { 64, 7, 12, 0, 0, }, /* 513 */ - { 64, 21, 12, 0, 0, }, /* 514 */ - { 74, 10, 5, 0, 0, }, /* 515 */ - { 74, 7, 12, 0, 0, }, /* 516 */ - { 74, 12, 3, 0, 0, }, /* 517 */ - { 74, 21, 12, 0, 0, }, /* 518 */ - { 74, 13, 12, 0, 0, }, /* 519 */ - { 68, 13, 12, 0, 0, }, /* 520 */ - { 68, 7, 12, 0, 0, }, /* 521 */ - { 68, 12, 3, 0, 0, }, /* 522 */ - { 68, 21, 12, 0, 0, }, /* 523 */ - { 73, 7, 12, 0, 0, }, /* 524 */ - { 73, 12, 3, 0, 0, }, /* 525 */ - { 73, 10, 5, 0, 0, }, /* 526 */ - { 73, 21, 12, 0, 0, }, /* 527 */ - { 83, 12, 3, 0, 0, }, /* 528 */ - { 83, 10, 5, 0, 0, }, /* 529 */ - { 83, 7, 12, 0, 0, }, /* 530 */ - { 83, 21, 12, 0, 0, }, /* 531 */ - { 83, 13, 12, 0, 0, }, /* 532 */ - { 38, 6, 12, 0, 0, }, /* 533 */ - { 67, 7, 12, 0, 0, }, /* 534 */ - { 67, 12, 3, 0, 0, }, /* 535 */ - { 67, 10, 5, 0, 0, }, /* 536 */ - { 67, 13, 12, 0, 0, }, /* 537 */ - { 67, 21, 12, 0, 0, }, /* 538 */ - { 91, 7, 12, 0, 0, }, /* 539 */ - { 91, 12, 3, 0, 0, }, /* 540 */ - { 91, 6, 12, 0, 0, }, /* 541 */ - { 91, 21, 12, 0, 0, }, /* 542 */ - { 86, 7, 12, 0, 0, }, /* 543 */ - { 86, 10, 5, 0, 0, }, /* 544 */ - { 86, 12, 3, 0, 0, }, /* 545 */ - { 86, 21, 12, 0, 0, }, /* 546 */ - { 86, 6, 12, 0, 0, }, /* 547 */ - { 86, 13, 12, 0, 0, }, /* 548 */ - { 23, 7, 9, 0, 0, }, /* 549 */ - { 23, 7, 10, 0, 0, }, /* 550 */ - { 9, 4, 2, 0, 0, }, /* 551 */ - { 9, 3, 12, 0, 0, }, /* 552 */ - { 25, 25, 12, 0, 0, }, /* 553 */ - { 0, 24, 12, 0, 0, }, /* 554 */ - { 9, 6, 3, 0, 0, }, /* 555 */ - { 35, 7, 12, 0, 0, }, /* 556 */ - { 19, 14, 12, 0, 0, }, /* 557 */ - { 19, 15, 12, 0, 0, }, /* 558 */ - { 19, 26, 12, 0, 0, }, /* 559 */ - { 70, 7, 12, 0, 0, }, /* 560 */ - { 66, 7, 12, 0, 0, }, /* 561 */ - { 41, 7, 12, 0, 0, }, /* 562 */ - { 41, 15, 12, 0, 0, }, /* 563 */ - { 18, 7, 12, 0, 0, }, /* 564 */ - { 18, 14, 12, 0, 0, }, /* 565 */ - { 117, 7, 12, 0, 0, }, /* 566 */ - { 117, 12, 3, 0, 0, }, /* 567 */ - { 59, 7, 12, 0, 0, }, /* 568 */ - { 59, 21, 12, 0, 0, }, /* 569 */ - { 42, 7, 12, 0, 0, }, /* 570 */ - { 42, 21, 12, 0, 0, }, /* 571 */ - { 42, 14, 12, 0, 0, }, /* 572 */ - { 13, 9, 12, 0, 40, }, /* 573 */ - { 13, 5, 12, 0, -40, }, /* 574 */ - { 46, 7, 12, 0, 0, }, /* 575 */ - { 44, 7, 12, 0, 0, }, /* 576 */ - { 44, 13, 12, 0, 0, }, /* 577 */ - { 105, 7, 12, 0, 0, }, /* 578 */ - { 103, 7, 12, 0, 0, }, /* 579 */ - { 103, 21, 12, 0, 0, }, /* 580 */ - { 109, 7, 12, 0, 0, }, /* 581 */ - { 11, 7, 12, 0, 0, }, /* 582 */ - { 80, 7, 12, 0, 0, }, /* 583 */ - { 80, 21, 12, 0, 0, }, /* 584 */ - { 80, 15, 12, 0, 0, }, /* 585 */ - { 119, 7, 12, 0, 0, }, /* 586 */ - { 119, 26, 12, 0, 0, }, /* 587 */ - { 119, 15, 12, 0, 0, }, /* 588 */ - { 115, 7, 12, 0, 0, }, /* 589 */ - { 115, 15, 12, 0, 0, }, /* 590 */ - { 65, 7, 12, 0, 0, }, /* 591 */ - { 65, 15, 12, 0, 0, }, /* 592 */ - { 65, 21, 12, 0, 0, }, /* 593 */ - { 71, 7, 12, 0, 0, }, /* 594 */ - { 71, 21, 12, 0, 0, }, /* 595 */ - { 97, 7, 12, 0, 0, }, /* 596 */ - { 96, 7, 12, 0, 0, }, /* 597 */ - { 30, 7, 12, 0, 0, }, /* 598 */ - { 30, 12, 3, 0, 0, }, /* 599 */ - { 30, 15, 12, 0, 0, }, /* 600 */ - { 30, 21, 12, 0, 0, }, /* 601 */ - { 87, 7, 12, 0, 0, }, /* 602 */ - { 87, 15, 12, 0, 0, }, /* 603 */ - { 87, 21, 12, 0, 0, }, /* 604 */ - { 116, 7, 12, 0, 0, }, /* 605 */ - { 116, 15, 12, 0, 0, }, /* 606 */ - { 111, 7, 12, 0, 0, }, /* 607 */ - { 111, 26, 12, 0, 0, }, /* 608 */ - { 111, 12, 3, 0, 0, }, /* 609 */ - { 111, 15, 12, 0, 0, }, /* 610 */ - { 111, 21, 12, 0, 0, }, /* 611 */ - { 77, 7, 12, 0, 0, }, /* 612 */ - { 77, 21, 12, 0, 0, }, /* 613 */ - { 82, 7, 12, 0, 0, }, /* 614 */ - { 82, 15, 12, 0, 0, }, /* 615 */ - { 81, 7, 12, 0, 0, }, /* 616 */ - { 81, 15, 12, 0, 0, }, /* 617 */ - { 120, 7, 12, 0, 0, }, /* 618 */ - { 120, 21, 12, 0, 0, }, /* 619 */ - { 120, 15, 12, 0, 0, }, /* 620 */ - { 88, 7, 12, 0, 0, }, /* 621 */ - { 0, 15, 12, 0, 0, }, /* 622 */ - { 93, 10, 5, 0, 0, }, /* 623 */ - { 93, 12, 3, 0, 0, }, /* 624 */ - { 93, 7, 12, 0, 0, }, /* 625 */ - { 93, 21, 12, 0, 0, }, /* 626 */ - { 93, 15, 12, 0, 0, }, /* 627 */ - { 93, 13, 12, 0, 0, }, /* 628 */ - { 84, 12, 3, 0, 0, }, /* 629 */ - { 84, 10, 5, 0, 0, }, /* 630 */ - { 84, 7, 12, 0, 0, }, /* 631 */ - { 84, 21, 12, 0, 0, }, /* 632 */ - { 84, 1, 2, 0, 0, }, /* 633 */ - { 100, 7, 12, 0, 0, }, /* 634 */ - { 100, 13, 12, 0, 0, }, /* 635 */ - { 95, 12, 3, 0, 0, }, /* 636 */ - { 95, 7, 12, 0, 0, }, /* 637 */ - { 95, 10, 5, 0, 0, }, /* 638 */ - { 95, 13, 12, 0, 0, }, /* 639 */ - { 95, 21, 12, 0, 0, }, /* 640 */ - { 110, 7, 12, 0, 0, }, /* 641 */ - { 110, 12, 3, 0, 0, }, /* 642 */ - { 110, 21, 12, 0, 0, }, /* 643 */ - { 99, 12, 3, 0, 0, }, /* 644 */ - { 99, 10, 5, 0, 0, }, /* 645 */ - { 99, 7, 12, 0, 0, }, /* 646 */ - { 99, 21, 12, 0, 0, }, /* 647 */ - { 99, 13, 12, 0, 0, }, /* 648 */ - { 47, 15, 12, 0, 0, }, /* 649 */ - { 107, 7, 12, 0, 0, }, /* 650 */ - { 107, 10, 5, 0, 0, }, /* 651 */ - { 107, 12, 3, 0, 0, }, /* 652 */ - { 107, 21, 12, 0, 0, }, /* 653 */ - { 108, 7, 12, 0, 0, }, /* 654 */ - { 108, 12, 3, 0, 0, }, /* 655 */ - { 108, 10, 5, 0, 0, }, /* 656 */ - { 108, 13, 12, 0, 0, }, /* 657 */ - { 106, 12, 3, 0, 0, }, /* 658 */ - { 106, 10, 5, 0, 0, }, /* 659 */ - { 106, 7, 12, 0, 0, }, /* 660 */ - { 106, 10, 3, 0, 0, }, /* 661 */ - { 123, 7, 12, 0, 0, }, /* 662 */ - { 123, 10, 3, 0, 0, }, /* 663 */ - { 123, 10, 5, 0, 0, }, /* 664 */ - { 123, 12, 3, 0, 0, }, /* 665 */ - { 123, 21, 12, 0, 0, }, /* 666 */ - { 123, 13, 12, 0, 0, }, /* 667 */ - { 122, 7, 12, 0, 0, }, /* 668 */ - { 122, 10, 3, 0, 0, }, /* 669 */ - { 122, 10, 5, 0, 0, }, /* 670 */ - { 122, 12, 3, 0, 0, }, /* 671 */ - { 122, 21, 12, 0, 0, }, /* 672 */ - { 113, 7, 12, 0, 0, }, /* 673 */ - { 113, 10, 5, 0, 0, }, /* 674 */ - { 113, 12, 3, 0, 0, }, /* 675 */ - { 113, 21, 12, 0, 0, }, /* 676 */ - { 113, 13, 12, 0, 0, }, /* 677 */ - { 101, 7, 12, 0, 0, }, /* 678 */ - { 101, 12, 3, 0, 0, }, /* 679 */ - { 101, 10, 5, 0, 0, }, /* 680 */ - { 101, 13, 12, 0, 0, }, /* 681 */ - { 124, 9, 12, 0, 32, }, /* 682 */ - { 124, 5, 12, 0, -32, }, /* 683 */ - { 124, 13, 12, 0, 0, }, /* 684 */ - { 124, 15, 12, 0, 0, }, /* 685 */ - { 124, 7, 12, 0, 0, }, /* 686 */ - { 121, 7, 12, 0, 0, }, /* 687 */ - { 62, 7, 12, 0, 0, }, /* 688 */ - { 62, 14, 12, 0, 0, }, /* 689 */ - { 62, 21, 12, 0, 0, }, /* 690 */ - { 79, 7, 12, 0, 0, }, /* 691 */ - { 114, 7, 12, 0, 0, }, /* 692 */ - { 114, 13, 12, 0, 0, }, /* 693 */ - { 114, 21, 12, 0, 0, }, /* 694 */ - { 102, 7, 12, 0, 0, }, /* 695 */ - { 102, 12, 3, 0, 0, }, /* 696 */ - { 102, 21, 12, 0, 0, }, /* 697 */ - { 118, 7, 12, 0, 0, }, /* 698 */ - { 118, 12, 3, 0, 0, }, /* 699 */ - { 118, 21, 12, 0, 0, }, /* 700 */ - { 118, 26, 12, 0, 0, }, /* 701 */ - { 118, 6, 12, 0, 0, }, /* 702 */ - { 118, 13, 12, 0, 0, }, /* 703 */ - { 118, 15, 12, 0, 0, }, /* 704 */ - { 98, 7, 12, 0, 0, }, /* 705 */ - { 98, 10, 5, 0, 0, }, /* 706 */ - { 98, 12, 3, 0, 0, }, /* 707 */ - { 98, 6, 12, 0, 0, }, /* 708 */ - { 104, 7, 12, 0, 0, }, /* 709 */ - { 104, 26, 12, 0, 0, }, /* 710 */ - { 104, 12, 3, 0, 0, }, /* 711 */ - { 104, 21, 12, 0, 0, }, /* 712 */ - { 9, 10, 3, 0, 0, }, /* 713 */ - { 19, 12, 3, 0, 0, }, /* 714 */ - { 112, 7, 12, 0, 0, }, /* 715 */ - { 112, 15, 12, 0, 0, }, /* 716 */ - { 112, 12, 3, 0, 0, }, /* 717 */ - { 9, 26, 11, 0, 0, }, /* 718 */ - { 26, 26, 12, 0, 0, }, /* 719 */ + { 0, 6, 12, 0, 0, }, /* 202 */ + { 49, 21, 12, 0, 0, }, /* 203 */ + { 49, 1, 2, 0, 0, }, /* 204 */ + { 49, 7, 12, 0, 0, }, /* 205 */ + { 49, 12, 3, 0, 0, }, /* 206 */ + { 55, 7, 12, 0, 0, }, /* 207 */ + { 55, 12, 3, 0, 0, }, /* 208 */ + { 63, 13, 12, 0, 0, }, /* 209 */ + { 63, 7, 12, 0, 0, }, /* 210 */ + { 63, 12, 3, 0, 0, }, /* 211 */ + { 63, 6, 12, 0, 0, }, /* 212 */ + { 63, 26, 12, 0, 0, }, /* 213 */ + { 63, 21, 12, 0, 0, }, /* 214 */ + { 89, 7, 12, 0, 0, }, /* 215 */ + { 89, 12, 3, 0, 0, }, /* 216 */ + { 89, 6, 12, 0, 0, }, /* 217 */ + { 89, 21, 12, 0, 0, }, /* 218 */ + { 94, 7, 12, 0, 0, }, /* 219 */ + { 94, 12, 3, 0, 0, }, /* 220 */ + { 94, 21, 12, 0, 0, }, /* 221 */ + { 14, 12, 3, 0, 0, }, /* 222 */ + { 14, 10, 5, 0, 0, }, /* 223 */ + { 14, 7, 12, 0, 0, }, /* 224 */ + { 14, 13, 12, 0, 0, }, /* 225 */ + { 14, 21, 12, 0, 0, }, /* 226 */ + { 14, 6, 12, 0, 0, }, /* 227 */ + { 2, 7, 12, 0, 0, }, /* 228 */ + { 2, 12, 3, 0, 0, }, /* 229 */ + { 2, 10, 5, 0, 0, }, /* 230 */ + { 2, 10, 3, 0, 0, }, /* 231 */ + { 2, 13, 12, 0, 0, }, /* 232 */ + { 2, 23, 12, 0, 0, }, /* 233 */ + { 2, 15, 12, 0, 0, }, /* 234 */ + { 2, 26, 12, 0, 0, }, /* 235 */ + { 21, 12, 3, 0, 0, }, /* 236 */ + { 21, 10, 5, 0, 0, }, /* 237 */ + { 21, 7, 12, 0, 0, }, /* 238 */ + { 21, 13, 12, 0, 0, }, /* 239 */ + { 20, 12, 3, 0, 0, }, /* 240 */ + { 20, 10, 5, 0, 0, }, /* 241 */ + { 20, 7, 12, 0, 0, }, /* 242 */ + { 20, 13, 12, 0, 0, }, /* 243 */ + { 20, 21, 12, 0, 0, }, /* 244 */ + { 20, 23, 12, 0, 0, }, /* 245 */ + { 43, 12, 3, 0, 0, }, /* 246 */ + { 43, 10, 5, 0, 0, }, /* 247 */ + { 43, 7, 12, 0, 0, }, /* 248 */ + { 43, 10, 3, 0, 0, }, /* 249 */ + { 43, 13, 12, 0, 0, }, /* 250 */ + { 43, 26, 12, 0, 0, }, /* 251 */ + { 43, 15, 12, 0, 0, }, /* 252 */ + { 53, 12, 3, 0, 0, }, /* 253 */ + { 53, 7, 12, 0, 0, }, /* 254 */ + { 53, 10, 3, 0, 0, }, /* 255 */ + { 53, 10, 5, 0, 0, }, /* 256 */ + { 53, 13, 12, 0, 0, }, /* 257 */ + { 53, 15, 12, 0, 0, }, /* 258 */ + { 53, 26, 12, 0, 0, }, /* 259 */ + { 53, 23, 12, 0, 0, }, /* 260 */ + { 54, 12, 3, 0, 0, }, /* 261 */ + { 54, 10, 5, 0, 0, }, /* 262 */ + { 54, 7, 12, 0, 0, }, /* 263 */ + { 54, 13, 12, 0, 0, }, /* 264 */ + { 54, 15, 12, 0, 0, }, /* 265 */ + { 54, 26, 12, 0, 0, }, /* 266 */ + { 28, 12, 3, 0, 0, }, /* 267 */ + { 28, 10, 5, 0, 0, }, /* 268 */ + { 28, 7, 12, 0, 0, }, /* 269 */ + { 28, 10, 3, 0, 0, }, /* 270 */ + { 28, 13, 12, 0, 0, }, /* 271 */ + { 36, 12, 3, 0, 0, }, /* 272 */ + { 36, 10, 5, 0, 0, }, /* 273 */ + { 36, 7, 12, 0, 0, }, /* 274 */ + { 36, 10, 3, 0, 0, }, /* 275 */ + { 36, 13, 12, 0, 0, }, /* 276 */ + { 36, 15, 12, 0, 0, }, /* 277 */ + { 36, 26, 12, 0, 0, }, /* 278 */ + { 47, 10, 5, 0, 0, }, /* 279 */ + { 47, 7, 12, 0, 0, }, /* 280 */ + { 47, 12, 3, 0, 0, }, /* 281 */ + { 47, 10, 3, 0, 0, }, /* 282 */ + { 47, 13, 12, 0, 0, }, /* 283 */ + { 47, 21, 12, 0, 0, }, /* 284 */ + { 56, 7, 12, 0, 0, }, /* 285 */ + { 56, 12, 3, 0, 0, }, /* 286 */ + { 56, 7, 5, 0, 0, }, /* 287 */ + { 56, 6, 12, 0, 0, }, /* 288 */ + { 56, 21, 12, 0, 0, }, /* 289 */ + { 56, 13, 12, 0, 0, }, /* 290 */ + { 32, 7, 12, 0, 0, }, /* 291 */ + { 32, 12, 3, 0, 0, }, /* 292 */ + { 32, 7, 5, 0, 0, }, /* 293 */ + { 32, 6, 12, 0, 0, }, /* 294 */ + { 32, 13, 12, 0, 0, }, /* 295 */ + { 57, 7, 12, 0, 0, }, /* 296 */ + { 57, 26, 12, 0, 0, }, /* 297 */ + { 57, 21, 12, 0, 0, }, /* 298 */ + { 57, 12, 3, 0, 0, }, /* 299 */ + { 57, 13, 12, 0, 0, }, /* 300 */ + { 57, 15, 12, 0, 0, }, /* 301 */ + { 57, 22, 12, 0, 0, }, /* 302 */ + { 57, 18, 12, 0, 0, }, /* 303 */ + { 57, 10, 5, 0, 0, }, /* 304 */ + { 38, 7, 12, 0, 0, }, /* 305 */ + { 38, 10, 12, 0, 0, }, /* 306 */ + { 38, 12, 3, 0, 0, }, /* 307 */ + { 38, 10, 5, 0, 0, }, /* 308 */ + { 38, 13, 12, 0, 0, }, /* 309 */ + { 38, 21, 12, 0, 0, }, /* 310 */ + { 38, 26, 12, 0, 0, }, /* 311 */ + { 16, 9, 12, 0, 7264, }, /* 312 */ + { 16, 7, 12, 0, 0, }, /* 313 */ + { 16, 6, 12, 0, 0, }, /* 314 */ + { 23, 7, 6, 0, 0, }, /* 315 */ + { 23, 7, 7, 0, 0, }, /* 316 */ + { 23, 7, 8, 0, 0, }, /* 317 */ + { 15, 7, 12, 0, 0, }, /* 318 */ + { 15, 12, 3, 0, 0, }, /* 319 */ + { 15, 21, 12, 0, 0, }, /* 320 */ + { 15, 15, 12, 0, 0, }, /* 321 */ + { 15, 26, 12, 0, 0, }, /* 322 */ + { 8, 9, 12, 0, 38864, }, /* 323 */ + { 8, 9, 12, 0, 8, }, /* 324 */ + { 8, 5, 12, 0, -8, }, /* 325 */ + { 7, 17, 12, 0, 0, }, /* 326 */ + { 7, 7, 12, 0, 0, }, /* 327 */ + { 7, 21, 12, 0, 0, }, /* 328 */ + { 40, 29, 12, 0, 0, }, /* 329 */ + { 40, 7, 12, 0, 0, }, /* 330 */ + { 40, 22, 12, 0, 0, }, /* 331 */ + { 40, 18, 12, 0, 0, }, /* 332 */ + { 45, 7, 12, 0, 0, }, /* 333 */ + { 45, 14, 12, 0, 0, }, /* 334 */ + { 50, 7, 12, 0, 0, }, /* 335 */ + { 50, 12, 3, 0, 0, }, /* 336 */ + { 24, 7, 12, 0, 0, }, /* 337 */ + { 24, 12, 3, 0, 0, }, /* 338 */ + { 6, 7, 12, 0, 0, }, /* 339 */ + { 6, 12, 3, 0, 0, }, /* 340 */ + { 51, 7, 12, 0, 0, }, /* 341 */ + { 51, 12, 3, 0, 0, }, /* 342 */ + { 31, 7, 12, 0, 0, }, /* 343 */ + { 31, 12, 3, 0, 0, }, /* 344 */ + { 31, 10, 5, 0, 0, }, /* 345 */ + { 31, 21, 12, 0, 0, }, /* 346 */ + { 31, 6, 12, 0, 0, }, /* 347 */ + { 31, 23, 12, 0, 0, }, /* 348 */ + { 31, 13, 12, 0, 0, }, /* 349 */ + { 31, 15, 12, 0, 0, }, /* 350 */ + { 37, 21, 12, 0, 0, }, /* 351 */ + { 37, 17, 12, 0, 0, }, /* 352 */ + { 37, 12, 3, 0, 0, }, /* 353 */ + { 37, 1, 2, 0, 0, }, /* 354 */ + { 37, 13, 12, 0, 0, }, /* 355 */ + { 37, 7, 12, 0, 0, }, /* 356 */ + { 37, 6, 12, 0, 0, }, /* 357 */ + { 34, 7, 12, 0, 0, }, /* 358 */ + { 34, 12, 3, 0, 0, }, /* 359 */ + { 34, 10, 5, 0, 0, }, /* 360 */ + { 34, 26, 12, 0, 0, }, /* 361 */ + { 34, 21, 12, 0, 0, }, /* 362 */ + { 34, 13, 12, 0, 0, }, /* 363 */ + { 52, 7, 12, 0, 0, }, /* 364 */ + { 39, 7, 12, 0, 0, }, /* 365 */ + { 39, 13, 12, 0, 0, }, /* 366 */ + { 39, 15, 12, 0, 0, }, /* 367 */ + { 39, 26, 12, 0, 0, }, /* 368 */ + { 31, 26, 12, 0, 0, }, /* 369 */ + { 5, 7, 12, 0, 0, }, /* 370 */ + { 5, 12, 3, 0, 0, }, /* 371 */ + { 5, 10, 5, 0, 0, }, /* 372 */ + { 5, 21, 12, 0, 0, }, /* 373 */ + { 90, 7, 12, 0, 0, }, /* 374 */ + { 90, 10, 5, 0, 0, }, /* 375 */ + { 90, 12, 3, 0, 0, }, /* 376 */ + { 90, 10, 12, 0, 0, }, /* 377 */ + { 90, 13, 12, 0, 0, }, /* 378 */ + { 90, 21, 12, 0, 0, }, /* 379 */ + { 90, 6, 12, 0, 0, }, /* 380 */ + { 27, 11, 3, 0, 0, }, /* 381 */ + { 61, 12, 3, 0, 0, }, /* 382 */ + { 61, 10, 5, 0, 0, }, /* 383 */ + { 61, 7, 12, 0, 0, }, /* 384 */ + { 61, 13, 12, 0, 0, }, /* 385 */ + { 61, 21, 12, 0, 0, }, /* 386 */ + { 61, 26, 12, 0, 0, }, /* 387 */ + { 75, 12, 3, 0, 0, }, /* 388 */ + { 75, 10, 5, 0, 0, }, /* 389 */ + { 75, 7, 12, 0, 0, }, /* 390 */ + { 75, 13, 12, 0, 0, }, /* 391 */ + { 92, 7, 12, 0, 0, }, /* 392 */ + { 92, 12, 3, 0, 0, }, /* 393 */ + { 92, 10, 5, 0, 0, }, /* 394 */ + { 92, 21, 12, 0, 0, }, /* 395 */ + { 69, 7, 12, 0, 0, }, /* 396 */ + { 69, 10, 5, 0, 0, }, /* 397 */ + { 69, 12, 3, 0, 0, }, /* 398 */ + { 69, 21, 12, 0, 0, }, /* 399 */ + { 69, 13, 12, 0, 0, }, /* 400 */ + { 72, 13, 12, 0, 0, }, /* 401 */ + { 72, 7, 12, 0, 0, }, /* 402 */ + { 72, 6, 12, 0, 0, }, /* 403 */ + { 72, 21, 12, 0, 0, }, /* 404 */ + { 75, 21, 12, 0, 0, }, /* 405 */ + { 9, 10, 5, 0, 0, }, /* 406 */ + { 9, 7, 12, 0, 0, }, /* 407 */ + { 12, 5, 12, 0, 0, }, /* 408 */ + { 12, 6, 12, 0, 0, }, /* 409 */ + { 33, 5, 12, 0, 35332, }, /* 410 */ + { 33, 5, 12, 0, 3814, }, /* 411 */ + { 33, 9, 12, 63, 1, }, /* 412 */ + { 33, 5, 12, 63, -1, }, /* 413 */ + { 33, 5, 12, 63, -58, }, /* 414 */ + { 33, 9, 12, 0, -7615, }, /* 415 */ + { 19, 5, 12, 0, 8, }, /* 416 */ + { 19, 9, 12, 0, -8, }, /* 417 */ + { 19, 5, 12, 0, 74, }, /* 418 */ + { 19, 5, 12, 0, 86, }, /* 419 */ + { 19, 5, 12, 0, 100, }, /* 420 */ + { 19, 5, 12, 0, 128, }, /* 421 */ + { 19, 5, 12, 0, 112, }, /* 422 */ + { 19, 5, 12, 0, 126, }, /* 423 */ + { 19, 8, 12, 0, -8, }, /* 424 */ + { 19, 5, 12, 0, 9, }, /* 425 */ + { 19, 9, 12, 0, -74, }, /* 426 */ + { 19, 8, 12, 0, -9, }, /* 427 */ + { 19, 5, 12, 21, -7173, }, /* 428 */ + { 19, 9, 12, 0, -86, }, /* 429 */ + { 19, 9, 12, 0, -100, }, /* 430 */ + { 19, 9, 12, 0, -112, }, /* 431 */ + { 19, 9, 12, 0, -128, }, /* 432 */ + { 19, 9, 12, 0, -126, }, /* 433 */ + { 27, 1, 3, 0, 0, }, /* 434 */ + { 9, 27, 2, 0, 0, }, /* 435 */ + { 9, 28, 2, 0, 0, }, /* 436 */ + { 9, 2, 2, 0, 0, }, /* 437 */ + { 9, 9, 12, 0, 0, }, /* 438 */ + { 9, 5, 12, 0, 0, }, /* 439 */ + { 19, 9, 12, 67, -7517, }, /* 440 */ + { 33, 9, 12, 71, -8383, }, /* 441 */ + { 33, 9, 12, 75, -8262, }, /* 442 */ + { 33, 9, 12, 0, 28, }, /* 443 */ + { 33, 5, 12, 0, -28, }, /* 444 */ + { 33, 14, 12, 0, 16, }, /* 445 */ + { 33, 14, 12, 0, -16, }, /* 446 */ + { 33, 14, 12, 0, 0, }, /* 447 */ + { 9, 26, 12, 0, 26, }, /* 448 */ + { 9, 26, 12, 0, -26, }, /* 449 */ + { 4, 26, 12, 0, 0, }, /* 450 */ + { 17, 9, 12, 0, 48, }, /* 451 */ + { 17, 5, 12, 0, -48, }, /* 452 */ + { 33, 9, 12, 0, -10743, }, /* 453 */ + { 33, 9, 12, 0, -3814, }, /* 454 */ + { 33, 9, 12, 0, -10727, }, /* 455 */ + { 33, 5, 12, 0, -10795, }, /* 456 */ + { 33, 5, 12, 0, -10792, }, /* 457 */ + { 33, 9, 12, 0, -10780, }, /* 458 */ + { 33, 9, 12, 0, -10749, }, /* 459 */ + { 33, 9, 12, 0, -10783, }, /* 460 */ + { 33, 9, 12, 0, -10782, }, /* 461 */ + { 33, 9, 12, 0, -10815, }, /* 462 */ + { 10, 5, 12, 0, 0, }, /* 463 */ + { 10, 26, 12, 0, 0, }, /* 464 */ + { 10, 12, 3, 0, 0, }, /* 465 */ + { 10, 21, 12, 0, 0, }, /* 466 */ + { 10, 15, 12, 0, 0, }, /* 467 */ + { 16, 5, 12, 0, -7264, }, /* 468 */ + { 58, 7, 12, 0, 0, }, /* 469 */ + { 58, 6, 12, 0, 0, }, /* 470 */ + { 58, 21, 12, 0, 0, }, /* 471 */ + { 58, 12, 3, 0, 0, }, /* 472 */ + { 22, 26, 12, 0, 0, }, /* 473 */ + { 22, 6, 12, 0, 0, }, /* 474 */ + { 22, 14, 12, 0, 0, }, /* 475 */ + { 23, 10, 3, 0, 0, }, /* 476 */ + { 26, 7, 12, 0, 0, }, /* 477 */ + { 26, 6, 12, 0, 0, }, /* 478 */ + { 29, 7, 12, 0, 0, }, /* 479 */ + { 29, 6, 12, 0, 0, }, /* 480 */ + { 3, 7, 12, 0, 0, }, /* 481 */ + { 23, 7, 12, 0, 0, }, /* 482 */ + { 23, 26, 12, 0, 0, }, /* 483 */ + { 29, 26, 12, 0, 0, }, /* 484 */ + { 22, 7, 12, 0, 0, }, /* 485 */ + { 60, 7, 12, 0, 0, }, /* 486 */ + { 60, 6, 12, 0, 0, }, /* 487 */ + { 60, 26, 12, 0, 0, }, /* 488 */ + { 85, 7, 12, 0, 0, }, /* 489 */ + { 85, 6, 12, 0, 0, }, /* 490 */ + { 85, 21, 12, 0, 0, }, /* 491 */ + { 76, 7, 12, 0, 0, }, /* 492 */ + { 76, 6, 12, 0, 0, }, /* 493 */ + { 76, 21, 12, 0, 0, }, /* 494 */ + { 76, 13, 12, 0, 0, }, /* 495 */ + { 12, 7, 12, 0, 0, }, /* 496 */ + { 12, 21, 12, 0, 0, }, /* 497 */ + { 78, 7, 12, 0, 0, }, /* 498 */ + { 78, 14, 12, 0, 0, }, /* 499 */ + { 78, 12, 3, 0, 0, }, /* 500 */ + { 78, 21, 12, 0, 0, }, /* 501 */ + { 33, 9, 12, 0, -35332, }, /* 502 */ + { 33, 9, 12, 0, -42280, }, /* 503 */ + { 33, 9, 12, 0, -42308, }, /* 504 */ + { 33, 9, 12, 0, -42319, }, /* 505 */ + { 33, 9, 12, 0, -42315, }, /* 506 */ + { 33, 9, 12, 0, -42305, }, /* 507 */ + { 33, 9, 12, 0, -42258, }, /* 508 */ + { 33, 9, 12, 0, -42282, }, /* 509 */ + { 33, 9, 12, 0, -42261, }, /* 510 */ + { 33, 9, 12, 0, 928, }, /* 511 */ + { 48, 7, 12, 0, 0, }, /* 512 */ + { 48, 12, 3, 0, 0, }, /* 513 */ + { 48, 10, 5, 0, 0, }, /* 514 */ + { 48, 26, 12, 0, 0, }, /* 515 */ + { 64, 7, 12, 0, 0, }, /* 516 */ + { 64, 21, 12, 0, 0, }, /* 517 */ + { 74, 10, 5, 0, 0, }, /* 518 */ + { 74, 7, 12, 0, 0, }, /* 519 */ + { 74, 12, 3, 0, 0, }, /* 520 */ + { 74, 21, 12, 0, 0, }, /* 521 */ + { 74, 13, 12, 0, 0, }, /* 522 */ + { 68, 13, 12, 0, 0, }, /* 523 */ + { 68, 7, 12, 0, 0, }, /* 524 */ + { 68, 12, 3, 0, 0, }, /* 525 */ + { 68, 21, 12, 0, 0, }, /* 526 */ + { 73, 7, 12, 0, 0, }, /* 527 */ + { 73, 12, 3, 0, 0, }, /* 528 */ + { 73, 10, 5, 0, 0, }, /* 529 */ + { 73, 21, 12, 0, 0, }, /* 530 */ + { 83, 12, 3, 0, 0, }, /* 531 */ + { 83, 10, 5, 0, 0, }, /* 532 */ + { 83, 7, 12, 0, 0, }, /* 533 */ + { 83, 21, 12, 0, 0, }, /* 534 */ + { 83, 13, 12, 0, 0, }, /* 535 */ + { 38, 6, 12, 0, 0, }, /* 536 */ + { 67, 7, 12, 0, 0, }, /* 537 */ + { 67, 12, 3, 0, 0, }, /* 538 */ + { 67, 10, 5, 0, 0, }, /* 539 */ + { 67, 13, 12, 0, 0, }, /* 540 */ + { 67, 21, 12, 0, 0, }, /* 541 */ + { 91, 7, 12, 0, 0, }, /* 542 */ + { 91, 12, 3, 0, 0, }, /* 543 */ + { 91, 6, 12, 0, 0, }, /* 544 */ + { 91, 21, 12, 0, 0, }, /* 545 */ + { 86, 7, 12, 0, 0, }, /* 546 */ + { 86, 10, 5, 0, 0, }, /* 547 */ + { 86, 12, 3, 0, 0, }, /* 548 */ + { 86, 21, 12, 0, 0, }, /* 549 */ + { 86, 6, 12, 0, 0, }, /* 550 */ + { 33, 5, 12, 0, -928, }, /* 551 */ + { 8, 5, 12, 0, -38864, }, /* 552 */ + { 86, 13, 12, 0, 0, }, /* 553 */ + { 23, 7, 9, 0, 0, }, /* 554 */ + { 23, 7, 10, 0, 0, }, /* 555 */ + { 9, 4, 2, 0, 0, }, /* 556 */ + { 9, 3, 12, 0, 0, }, /* 557 */ + { 25, 25, 12, 0, 0, }, /* 558 */ + { 0, 24, 12, 0, 0, }, /* 559 */ + { 9, 6, 3, 0, 0, }, /* 560 */ + { 35, 7, 12, 0, 0, }, /* 561 */ + { 19, 14, 12, 0, 0, }, /* 562 */ + { 19, 15, 12, 0, 0, }, /* 563 */ + { 19, 26, 12, 0, 0, }, /* 564 */ + { 70, 7, 12, 0, 0, }, /* 565 */ + { 66, 7, 12, 0, 0, }, /* 566 */ + { 41, 7, 12, 0, 0, }, /* 567 */ + { 41, 15, 12, 0, 0, }, /* 568 */ + { 18, 7, 12, 0, 0, }, /* 569 */ + { 18, 14, 12, 0, 0, }, /* 570 */ + { 117, 7, 12, 0, 0, }, /* 571 */ + { 117, 12, 3, 0, 0, }, /* 572 */ + { 59, 7, 12, 0, 0, }, /* 573 */ + { 59, 21, 12, 0, 0, }, /* 574 */ + { 42, 7, 12, 0, 0, }, /* 575 */ + { 42, 21, 12, 0, 0, }, /* 576 */ + { 42, 14, 12, 0, 0, }, /* 577 */ + { 13, 9, 12, 0, 40, }, /* 578 */ + { 13, 5, 12, 0, -40, }, /* 579 */ + { 46, 7, 12, 0, 0, }, /* 580 */ + { 44, 7, 12, 0, 0, }, /* 581 */ + { 44, 13, 12, 0, 0, }, /* 582 */ + { 105, 7, 12, 0, 0, }, /* 583 */ + { 103, 7, 12, 0, 0, }, /* 584 */ + { 103, 21, 12, 0, 0, }, /* 585 */ + { 109, 7, 12, 0, 0, }, /* 586 */ + { 11, 7, 12, 0, 0, }, /* 587 */ + { 80, 7, 12, 0, 0, }, /* 588 */ + { 80, 21, 12, 0, 0, }, /* 589 */ + { 80, 15, 12, 0, 0, }, /* 590 */ + { 119, 7, 12, 0, 0, }, /* 591 */ + { 119, 26, 12, 0, 0, }, /* 592 */ + { 119, 15, 12, 0, 0, }, /* 593 */ + { 115, 7, 12, 0, 0, }, /* 594 */ + { 115, 15, 12, 0, 0, }, /* 595 */ + { 127, 7, 12, 0, 0, }, /* 596 */ + { 127, 15, 12, 0, 0, }, /* 597 */ + { 65, 7, 12, 0, 0, }, /* 598 */ + { 65, 15, 12, 0, 0, }, /* 599 */ + { 65, 21, 12, 0, 0, }, /* 600 */ + { 71, 7, 12, 0, 0, }, /* 601 */ + { 71, 21, 12, 0, 0, }, /* 602 */ + { 97, 7, 12, 0, 0, }, /* 603 */ + { 96, 7, 12, 0, 0, }, /* 604 */ + { 96, 15, 12, 0, 0, }, /* 605 */ + { 30, 7, 12, 0, 0, }, /* 606 */ + { 30, 12, 3, 0, 0, }, /* 607 */ + { 30, 15, 12, 0, 0, }, /* 608 */ + { 30, 21, 12, 0, 0, }, /* 609 */ + { 87, 7, 12, 0, 0, }, /* 610 */ + { 87, 15, 12, 0, 0, }, /* 611 */ + { 87, 21, 12, 0, 0, }, /* 612 */ + { 116, 7, 12, 0, 0, }, /* 613 */ + { 116, 15, 12, 0, 0, }, /* 614 */ + { 111, 7, 12, 0, 0, }, /* 615 */ + { 111, 26, 12, 0, 0, }, /* 616 */ + { 111, 12, 3, 0, 0, }, /* 617 */ + { 111, 15, 12, 0, 0, }, /* 618 */ + { 111, 21, 12, 0, 0, }, /* 619 */ + { 77, 7, 12, 0, 0, }, /* 620 */ + { 77, 21, 12, 0, 0, }, /* 621 */ + { 82, 7, 12, 0, 0, }, /* 622 */ + { 82, 15, 12, 0, 0, }, /* 623 */ + { 81, 7, 12, 0, 0, }, /* 624 */ + { 81, 15, 12, 0, 0, }, /* 625 */ + { 120, 7, 12, 0, 0, }, /* 626 */ + { 120, 21, 12, 0, 0, }, /* 627 */ + { 120, 15, 12, 0, 0, }, /* 628 */ + { 88, 7, 12, 0, 0, }, /* 629 */ + { 129, 9, 12, 0, 64, }, /* 630 */ + { 129, 5, 12, 0, -64, }, /* 631 */ + { 129, 15, 12, 0, 0, }, /* 632 */ + { 0, 15, 12, 0, 0, }, /* 633 */ + { 93, 10, 5, 0, 0, }, /* 634 */ + { 93, 12, 3, 0, 0, }, /* 635 */ + { 93, 7, 12, 0, 0, }, /* 636 */ + { 93, 21, 12, 0, 0, }, /* 637 */ + { 93, 15, 12, 0, 0, }, /* 638 */ + { 93, 13, 12, 0, 0, }, /* 639 */ + { 84, 12, 3, 0, 0, }, /* 640 */ + { 84, 10, 5, 0, 0, }, /* 641 */ + { 84, 7, 12, 0, 0, }, /* 642 */ + { 84, 21, 12, 0, 0, }, /* 643 */ + { 84, 1, 2, 0, 0, }, /* 644 */ + { 100, 7, 12, 0, 0, }, /* 645 */ + { 100, 13, 12, 0, 0, }, /* 646 */ + { 95, 12, 3, 0, 0, }, /* 647 */ + { 95, 7, 12, 0, 0, }, /* 648 */ + { 95, 10, 5, 0, 0, }, /* 649 */ + { 95, 13, 12, 0, 0, }, /* 650 */ + { 95, 21, 12, 0, 0, }, /* 651 */ + { 110, 7, 12, 0, 0, }, /* 652 */ + { 110, 12, 3, 0, 0, }, /* 653 */ + { 110, 21, 12, 0, 0, }, /* 654 */ + { 99, 12, 3, 0, 0, }, /* 655 */ + { 99, 10, 5, 0, 0, }, /* 656 */ + { 99, 7, 12, 0, 0, }, /* 657 */ + { 99, 21, 12, 0, 0, }, /* 658 */ + { 99, 13, 12, 0, 0, }, /* 659 */ + { 47, 15, 12, 0, 0, }, /* 660 */ + { 107, 7, 12, 0, 0, }, /* 661 */ + { 107, 10, 5, 0, 0, }, /* 662 */ + { 107, 12, 3, 0, 0, }, /* 663 */ + { 107, 21, 12, 0, 0, }, /* 664 */ + { 128, 7, 12, 0, 0, }, /* 665 */ + { 128, 21, 12, 0, 0, }, /* 666 */ + { 108, 7, 12, 0, 0, }, /* 667 */ + { 108, 12, 3, 0, 0, }, /* 668 */ + { 108, 10, 5, 0, 0, }, /* 669 */ + { 108, 13, 12, 0, 0, }, /* 670 */ + { 106, 12, 3, 0, 0, }, /* 671 */ + { 106, 10, 5, 0, 0, }, /* 672 */ + { 106, 7, 12, 0, 0, }, /* 673 */ + { 106, 10, 3, 0, 0, }, /* 674 */ + { 123, 7, 12, 0, 0, }, /* 675 */ + { 123, 10, 3, 0, 0, }, /* 676 */ + { 123, 10, 5, 0, 0, }, /* 677 */ + { 123, 12, 3, 0, 0, }, /* 678 */ + { 123, 21, 12, 0, 0, }, /* 679 */ + { 123, 13, 12, 0, 0, }, /* 680 */ + { 122, 7, 12, 0, 0, }, /* 681 */ + { 122, 10, 3, 0, 0, }, /* 682 */ + { 122, 10, 5, 0, 0, }, /* 683 */ + { 122, 12, 3, 0, 0, }, /* 684 */ + { 122, 21, 12, 0, 0, }, /* 685 */ + { 113, 7, 12, 0, 0, }, /* 686 */ + { 113, 10, 5, 0, 0, }, /* 687 */ + { 113, 12, 3, 0, 0, }, /* 688 */ + { 113, 21, 12, 0, 0, }, /* 689 */ + { 113, 13, 12, 0, 0, }, /* 690 */ + { 101, 7, 12, 0, 0, }, /* 691 */ + { 101, 12, 3, 0, 0, }, /* 692 */ + { 101, 10, 5, 0, 0, }, /* 693 */ + { 101, 13, 12, 0, 0, }, /* 694 */ + { 125, 7, 12, 0, 0, }, /* 695 */ + { 125, 12, 3, 0, 0, }, /* 696 */ + { 125, 10, 5, 0, 0, }, /* 697 */ + { 125, 13, 12, 0, 0, }, /* 698 */ + { 125, 15, 12, 0, 0, }, /* 699 */ + { 125, 21, 12, 0, 0, }, /* 700 */ + { 125, 26, 12, 0, 0, }, /* 701 */ + { 124, 9, 12, 0, 32, }, /* 702 */ + { 124, 5, 12, 0, -32, }, /* 703 */ + { 124, 13, 12, 0, 0, }, /* 704 */ + { 124, 15, 12, 0, 0, }, /* 705 */ + { 124, 7, 12, 0, 0, }, /* 706 */ + { 121, 7, 12, 0, 0, }, /* 707 */ + { 62, 7, 12, 0, 0, }, /* 708 */ + { 62, 14, 12, 0, 0, }, /* 709 */ + { 62, 21, 12, 0, 0, }, /* 710 */ + { 79, 7, 12, 0, 0, }, /* 711 */ + { 126, 7, 12, 0, 0, }, /* 712 */ + { 114, 7, 12, 0, 0, }, /* 713 */ + { 114, 13, 12, 0, 0, }, /* 714 */ + { 114, 21, 12, 0, 0, }, /* 715 */ + { 102, 7, 12, 0, 0, }, /* 716 */ + { 102, 12, 3, 0, 0, }, /* 717 */ + { 102, 21, 12, 0, 0, }, /* 718 */ + { 118, 7, 12, 0, 0, }, /* 719 */ + { 118, 12, 3, 0, 0, }, /* 720 */ + { 118, 21, 12, 0, 0, }, /* 721 */ + { 118, 26, 12, 0, 0, }, /* 722 */ + { 118, 6, 12, 0, 0, }, /* 723 */ + { 118, 13, 12, 0, 0, }, /* 724 */ + { 118, 15, 12, 0, 0, }, /* 725 */ + { 98, 7, 12, 0, 0, }, /* 726 */ + { 98, 10, 5, 0, 0, }, /* 727 */ + { 98, 12, 3, 0, 0, }, /* 728 */ + { 98, 6, 12, 0, 0, }, /* 729 */ + { 104, 7, 12, 0, 0, }, /* 730 */ + { 104, 26, 12, 0, 0, }, /* 731 */ + { 104, 12, 3, 0, 0, }, /* 732 */ + { 104, 21, 12, 0, 0, }, /* 733 */ + { 9, 10, 3, 0, 0, }, /* 734 */ + { 19, 12, 3, 0, 0, }, /* 735 */ + { 130, 26, 12, 0, 0, }, /* 736 */ + { 130, 12, 3, 0, 0, }, /* 737 */ + { 130, 21, 12, 0, 0, }, /* 738 */ + { 112, 7, 12, 0, 0, }, /* 739 */ + { 112, 15, 12, 0, 0, }, /* 740 */ + { 112, 12, 3, 0, 0, }, /* 741 */ + { 9, 26, 11, 0, 0, }, /* 742 */ + { 26, 26, 12, 0, 0, }, /* 743 */ }; const uint8_t PRIV(ucd_stage1)[] = { /* 8704 bytes */ @@ -839,19 +863,19 @@ const uint8_t PRIV(ucd_stage1)[] = { /* 8704 bytes */ 123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F000 */ 123,123, 95, 95,124,125,126,127,128,128,129,130,131,132,133,134, /* U+F800 */ 135,136,137,138,139,140,141,142,143,144,145,139,146,146,147,139, /* U+10000 */ -148,149,150,151,152,153,154,155,156,139,139,139,157,139,139,139, /* U+10800 */ -158,159,160,161,162,163,164,139,139,165,139,166,167,168,139,139, /* U+11000 */ -139,169,139,139,139,170,139,139,139,139,139,139,139,139,139,139, /* U+11800 */ -171,171,171,171,171,171,171,172,173,139,139,139,139,139,139,139, /* U+12000 */ +148,149,150,151,152,153,154,155,156,157,139,139,158,139,139,139, /* U+10800 */ +159,160,161,162,163,164,165,139,139,166,139,167,168,169,170,139, /* U+11000 */ +139,171,139,139,139,172,139,139,139,139,139,139,139,139,139,139, /* U+11800 */ +173,173,173,173,173,173,173,174,175,173,176,139,139,139,139,139, /* U+12000 */ 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+12800 */ -174,174,174,174,174,174,174,174,175,139,139,139,139,139,139,139, /* U+13000 */ +177,177,177,177,177,177,177,177,178,139,139,139,139,139,139,139, /* U+13000 */ 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+13800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+14000 */ +139,139,139,139,139,139,139,139,179,179,179,179,180,139,139,139, /* U+14000 */ 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+14800 */ 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+15000 */ 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+15800 */ 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+16000 */ -176,176,176,176,177,178,179,180,139,139,139,139,139,139,181,182, /* U+16800 */ +181,181,181,181,182,183,184,185,139,139,139,139,139,139,186,187, /* U+16800 */ 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+17000 */ 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+17800 */ 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+18000 */ @@ -860,16 +884,16 @@ const uint8_t PRIV(ucd_stage1)[] = { /* 8704 bytes */ 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+19800 */ 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+1A000 */ 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+1A800 */ -183,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+1B000 */ -139,139,139,139,139,139,139,139,184,185,139,139,139,139,139,139, /* U+1B800 */ +188,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+1B000 */ +139,139,139,139,139,139,139,139,189,190,139,139,139,139,139,139, /* U+1B800 */ 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+1C000 */ 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+1C800 */ - 71,186,187,188,189,139,190,139,191,192,193,194,195,196,197,198, /* U+1D000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+1D800 */ + 71,191,192,193,194,139,195,139,196,197,198,199,200,201,202,203, /* U+1D000 */ +204,204,204,204,205,206,139,139,139,139,139,139,139,139,139,139, /* U+1D800 */ 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+1E000 */ -199,200,139,139,139,139,139,139,139,139,139,139,201,202,139,139, /* U+1E800 */ -203,204,205,206,207,139,208,209, 71,210,211,212,213,214,215,216, /* U+1F000 */ -217,218,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+1F800 */ +207,208,139,139,139,139,139,139,139,139,139,139,209,210,139,139, /* U+1E800 */ +211,212,213,214,215,139, 71,216, 71, 71,217,218, 71,219,220,221, /* U+1F000 */ +222,223,224,225,139,139,139,139,139,139,139,139,139,139,139,139, /* U+1F800 */ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+20000 */ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+20800 */ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+21000 */ @@ -890,18 +914,18 @@ const uint8_t PRIV(ucd_stage1)[] = { /* 8704 bytes */ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+28800 */ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+29000 */ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+29800 */ - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,219, 95, 95, /* U+2A000 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,226, 95, 95, /* U+2A000 */ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+2A800 */ - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,220, 95, /* U+2B000 */ -221,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+2B800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+2C000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+2C800 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,227, 95, /* U+2B000 */ +228, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+2B800 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+2C000 */ + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,229,139,139, /* U+2C800 */ 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+2D000 */ 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+2D800 */ 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+2E000 */ 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+2E800 */ 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+2F000 */ - 95, 95, 95, 95,221,139,139,139,139,139,139,139,139,139,139,139, /* U+2F800 */ + 95, 95, 95, 95,230,139,139,139,139,139,139,139,139,139,139,139, /* U+2F800 */ 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+30000 */ 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+30800 */ 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+31000 */ @@ -1254,8 +1278,8 @@ const uint8_t PRIV(ucd_stage1)[] = { /* 8704 bytes */ 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+DE800 */ 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+DF000 */ 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+DF800 */ -222,223,224,225,223,223,223,223,223,223,223,223,223,223,223,223, /* U+E0000 */ -223,223,223,223,223,223,223,223,223,223,223,223,223,223,223,223, /* U+E0800 */ +231,232,233,234,232,232,232,232,232,232,232,232,232,232,232,232, /* U+E0000 */ +232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232, /* U+E0800 */ 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E1000 */ 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E1800 */ 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E2000 */ @@ -1317,7 +1341,7 @@ const uint8_t PRIV(ucd_stage1)[] = { /* 8704 bytes */ 123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FE000 */ 123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FE800 */ 123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FF000 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,226, /* U+FF800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,235, /* U+FF800 */ 123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+100000 */ 123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+100800 */ 123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+101000 */ @@ -1349,10 +1373,10 @@ const uint8_t PRIV(ucd_stage1)[] = { /* 8704 bytes */ 123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10E000 */ 123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10E800 */ 123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10F000 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,226, /* U+10F800 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,235, /* U+10F800 */ }; -const uint16_t PRIV(ucd_stage2)[] = { /* 58112 bytes, block = 128 */ +const uint16_t PRIV(ucd_stage2)[] = { /* 60416 bytes, block = 128 */ /* block 0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1405,533 +1429,533 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 58112 bytes, block = 128 */ /* block 5 */ 99, 33, 33, 99, 33, 33, 33,100, 99,101,102,102,103, 33, 33, 33, - 33, 33,104, 33, 20, 33, 33, 33, 33, 33, 33, 33, 33, 33,105, 33, + 33, 33,104, 33, 20, 33, 33, 33, 33, 33, 33, 33, 33,105,106, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, -106,106,106,106,106,106,106,106,106,107,107,107,107,107,107,107, -107,107, 14, 14, 14, 14,107,107,107,107,107,107,107,107,107,107, -107,107, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, -106,106,106,106,106, 14, 14, 14, 14, 14,108,108,107, 14,107, 14, +107,107,107,107,107,107,107,107,107,108,108,108,108,108,108,108, +108,108, 14, 14, 14, 14,108,108,108,108,108,108,108,108,108,108, +108,108, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +107,107,107,107,107, 14, 14, 14, 14, 14,109,109,108, 14,108, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, /* block 6 */ -109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, -109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, -109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, -109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, -109,109,109,109,109,110,109,109,109,109,109,109,109,109,109,109, -109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, -109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, -111,112,111,112,107,113,111,112,114,114,115,116,116,116, 4,117, +110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, +110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, +110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, +110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, +110,110,110,110,110,111,110,110,110,110,110,110,110,110,110,110, +110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, +110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, +112,113,112,113,108,114,112,113,115,115,116,117,117,117, 4,118, /* block 7 */ -114,114,114,114,113, 14,118, 4,119,119,119,114,120,114,121,121, -122,123,124,123,123,125,123,123,126,127,128,123,129,123,123,123, -130,131,114,132,123,123,133,123,123,134,123,123,135,136,136,136, -122,137,138,137,137,139,137,137,140,141,142,137,143,137,137,137, -144,145,146,147,137,137,148,137,137,149,137,137,150,151,151,152, -153,154,155,155,155,156,157,158,111,112,111,112,111,112,111,112, -111,112,159,160,159,160,159,160,159,160,159,160,159,160,159,160, -161,162,163,164,165,166,167,111,112,168,111,112,122,169,169,169, +115,115,115,115,114, 14,119, 4,120,120,120,115,121,115,122,122, +123,124,125,124,124,126,124,124,127,128,129,124,130,124,124,124, +131,132,115,133,124,124,134,124,124,135,124,124,136,137,137,137, +123,138,139,138,138,140,138,138,141,142,143,138,144,138,138,138, +145,146,147,148,138,138,149,138,138,150,138,138,151,152,152,153, +154,155,156,156,156,157,158,159,112,113,112,113,112,113,112,113, +112,113,160,161,160,161,160,161,160,161,160,161,160,161,160,161, +162,163,164,165,166,167,168,112,113,169,112,113,123,170,170,170, /* block 8 */ -170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170, -171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171, 171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171, 172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172, 172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172, 173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173, -174,175,174,175,174,175,174,175,174,175,174,175,174,175,174,175, -174,175,174,175,174,175,174,175,174,175,174,175,174,175,174,175, +173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173, +174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174, +175,176,175,176,175,176,175,176,175,176,175,176,175,176,175,176, +175,176,175,176,175,176,175,176,175,176,175,176,175,176,175,176, /* block 9 */ -174,175,176,177,177,109,109,177,178,178,174,175,174,175,174,175, -174,175,174,175,174,175,174,175,174,175,174,175,174,175,174,175, -174,175,174,175,174,175,174,175,174,175,174,175,174,175,174,175, -174,175,174,175,174,175,174,175,174,175,174,175,174,175,174,175, -179,174,175,174,175,174,175,174,175,174,175,174,175,174,175,180, -174,175,174,175,174,175,174,175,174,175,174,175,174,175,174,175, -174,175,174,175,174,175,174,175,174,175,174,175,174,175,174,175, -174,175,174,175,174,175,174,175,174,175,174,175,174,175,174,175, +175,176,177,178,178,110,110,178,179,179,175,176,175,176,175,176, +175,176,175,176,175,176,175,176,175,176,175,176,175,176,175,176, +175,176,175,176,175,176,175,176,175,176,175,176,175,176,175,176, +175,176,175,176,175,176,175,176,175,176,175,176,175,176,175,176, +180,175,176,175,176,175,176,175,176,175,176,175,176,175,176,181, +175,176,175,176,175,176,175,176,175,176,175,176,175,176,175,176, +175,176,175,176,175,176,175,176,175,176,175,176,175,176,175,176, +175,176,175,176,175,176,175,176,175,176,175,176,175,176,175,176, /* block 10 */ -174,175,174,175,174,175,174,175,174,175,174,175,174,175,174,175, -174,175,174,175,174,175,174,175,174,175,174,175,174,175,174,175, -174,175,174,175,174,175,174,175,174,175,174,175,174,175,174,175, -114,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181, -181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181, -181,181,181,181,181,181,181,114,114,182,183,183,183,183,183,183, -114,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184, -184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184, +175,176,175,176,175,176,175,176,175,176,175,176,175,176,175,176, +175,176,175,176,175,176,175,176,175,176,175,176,175,176,175,176, +175,176,175,176,175,176,175,176,175,176,175,176,175,176,175,176, +115,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182, +182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182, +182,182,182,182,182,182,182,115,115,183,184,184,184,184,184,184, +115,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185, +185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185, /* block 11 */ -184,184,184,184,184,184,184,185,114, 4,186,114,114,187,187,188, -114,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189, -189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189, -189,189,189,189,189,189,189,189,189,189,189,189,189,189,190,189, -191,189,189,191,189,189,191,189,114,114,114,114,114,114,114,114, -192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192, -192,192,192,192,192,192,192,192,192,192,192,114,114,114,114,114, -192,192,192,191,191,114,114,114,114,114,114,114,114,114,114,114, +185,185,185,185,185,185,185,186,115, 4,187,115,115,188,188,189, +115,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190, +190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190, +190,190,190,190,190,190,190,190,190,190,190,190,190,190,191,190, +192,190,190,192,190,190,192,190,115,115,115,115,115,115,115,115, +193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193, +193,193,193,193,193,193,193,193,193,193,193,115,115,115,115,115, +193,193,193,192,192,115,115,115,115,115,115,115,115,115,115,115, /* block 12 */ -193,193,193,193,193, 22,194,194,194,195,195,196, 4,195,197,197, -198,198,198,198,198,198,198,198,198,198,198, 4, 22,114,195, 4, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -107,199,199,199,199,199,199,199,199,199,199,109,109,109,109,109, -109,109,109,109,109,109,198,198,198,198,198,198,198,198,198,198, - 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,195,195,195,195,199,199, -109,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, +194,194,194,194,194, 22,195,195,195,196,196,197, 4,196,198,198, +199,199,199,199,199,199,199,199,199,199,199, 4, 22,115,196, 4, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +108,200,200,200,200,200,200,200,200,200,200,110,110,110,110,110, +110,110,110,110,110,110,199,199,199,199,199,199,199,199,199,199, +201,201,201,201,201,201,201,201,201,201,196,196,196,196,200,200, +110,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, /* block 13 */ -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,195,199,198,198,198,198,198,198,198, 22,197,198, -198,198,198,198,198,200,200,198,198,197,198,198,198,198,199,199, -201,201,201,201,201,201,201,201,201,201,199,199,199,197,197,199, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,196,200,199,199,199,199,199,199,199, 22,198,199, +199,199,199,199,199,202,202,199,199,198,199,199,199,199,200,200, +201,201,201,201,201,201,201,201,201,201,200,200,200,198,198,200, /* block 14 */ -202,202,202,202,202,202,202,202,202,202,202,202,202,202,114,203, -204,205,204,204,204,204,204,204,204,204,204,204,204,204,204,204, -204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204, +203,203,203,203,203,203,203,203,203,203,203,203,203,203,115,204, +205,206,205,205,205,205,205,205,205,205,205,205,205,205,205,205, 205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205, -205,205,205,205,205,205,205,205,205,205,205,114,114,204,204,204, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, +206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206, +206,206,206,206,206,206,206,206,206,206,206,115,115,205,205,205, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, /* block 15 */ -206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206, -206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206, -206,206,206,206,206,206,207,207,207,207,207,207,207,207,207,207, -207,206,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -208,208,208,208,208,208,208,208,208,208,209,209,209,209,209,209, -209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209, -209,209,209,209,209,209,209,209,209,209,209,210,210,210,210,210, -210,210,210,210,211,211,212,213,213,213,211,114,114,114,114,114, +207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207, +207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207, +207,207,207,207,207,207,208,208,208,208,208,208,208,208,208,208, +208,207,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +209,209,209,209,209,209,209,209,209,209,210,210,210,210,210,210, +210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210, +210,210,210,210,210,210,210,210,210,210,210,211,211,211,211,211, +211,211,211,211,212,212,213,214,214,214,212,115,115,115,115,115, /* block 16 */ -214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214, -214,214,214,214,214,214,215,215,215,215,216,215,215,215,215,215, -215,215,215,215,216,215,215,215,216,215,215,215,215,215,114,114, -217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,114, -218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218, -218,218,218,218,218,218,218,218,218,219,219,219,114,114,220,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +215,215,215,215,215,215,215,215,215,215,215,215,215,215,215,215, +215,215,215,215,215,215,216,216,216,216,217,216,216,216,216,216, +216,216,216,216,217,216,216,216,217,216,216,216,216,216,115,115, +218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,115, +219,219,219,219,219,219,219,219,219,219,219,219,219,219,219,219, +219,219,219,219,219,219,219,219,219,220,220,220,115,115,221,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 17 */ -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,199,199,199,199,199,199,199,199,199,199,199,199,199, 199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,198,198,198,198,198,198,198,198,198,198,198,198, -198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198, /* block 18 */ -221,221,221,222,223,223,223,223,223,223,223,223,223,223,223,223, -223,223,223,223,223,223,223,223,223,223,223,223,223,223,223,223, -223,223,223,223,223,223,223,223,223,223,223,223,223,223,223,223, -223,223,223,223,223,223,223,223,223,223,221,222,221,223,222,222, -222,221,221,221,221,221,221,221,221,222,222,222,222,221,222,222, -223,109,109,221,221,221,221,221,223,223,223,223,223,223,223,223, -223,223,221,221, 4, 4,224,224,224,224,224,224,224,224,224,224, -225,226,223,223,223,223,223,223,223,223,223,223,223,223,223,223, +222,222,222,223,224,224,224,224,224,224,224,224,224,224,224,224, +224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224, +224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224, +224,224,224,224,224,224,224,224,224,224,222,223,222,224,223,223, +223,222,222,222,222,222,222,222,222,223,223,223,223,222,223,223, +224,110,110,222,222,222,222,222,224,224,224,224,224,224,224,224, +224,224,222,222, 4, 4,225,225,225,225,225,225,225,225,225,225, +226,227,224,224,224,224,224,224,224,224,224,224,224,224,224,224, /* block 19 */ -227,228,229,229,114,227,227,227,227,227,227,227,227,114,114,227, -227,114,114,227,227,227,227,227,227,227,227,227,227,227,227,227, -227,227,227,227,227,227,227,227,227,114,227,227,227,227,227,227, -227,114,227,114,114,114,227,227,227,227,114,114,228,227,230,229, -229,228,228,228,228,114,114,229,229,114,114,229,229,228,227,114, -114,114,114,114,114,114,114,230,114,114,114,114,227,227,114,227, -227,227,228,228,114,114,231,231,231,231,231,231,231,231,231,231, -227,227,232,232,233,233,233,233,233,233,234,232,114,114,114,114, +228,229,230,230,115,228,228,228,228,228,228,228,228,115,115,228, +228,115,115,228,228,228,228,228,228,228,228,228,228,228,228,228, +228,228,228,228,228,228,228,228,228,115,228,228,228,228,228,228, +228,115,228,115,115,115,228,228,228,228,115,115,229,228,231,230, +230,229,229,229,229,115,115,230,230,115,115,230,230,229,228,115, +115,115,115,115,115,115,115,231,115,115,115,115,228,228,115,228, +228,228,229,229,115,115,232,232,232,232,232,232,232,232,232,232, +228,228,233,233,234,234,234,234,234,234,235,233,115,115,115,115, /* block 20 */ -114,235,235,236,114,237,237,237,237,237,237,114,114,114,114,237, -237,114,114,237,237,237,237,237,237,237,237,237,237,237,237,237, -237,237,237,237,237,237,237,237,237,114,237,237,237,237,237,237, -237,114,237,237,114,237,237,114,237,237,114,114,235,114,236,236, -236,235,235,114,114,114,114,235,235,114,114,235,235,235,114,114, -114,235,114,114,114,114,114,114,114,237,237,237,237,114,237,114, -114,114,114,114,114,114,238,238,238,238,238,238,238,238,238,238, -235,235,237,237,237,235,114,114,114,114,114,114,114,114,114,114, +115,236,236,237,115,238,238,238,238,238,238,115,115,115,115,238, +238,115,115,238,238,238,238,238,238,238,238,238,238,238,238,238, +238,238,238,238,238,238,238,238,238,115,238,238,238,238,238,238, +238,115,238,238,115,238,238,115,238,238,115,115,236,115,237,237, +237,236,236,115,115,115,115,236,236,115,115,236,236,236,115,115, +115,236,115,115,115,115,115,115,115,238,238,238,238,115,238,115, +115,115,115,115,115,115,239,239,239,239,239,239,239,239,239,239, +236,236,238,238,238,236,115,115,115,115,115,115,115,115,115,115, /* block 21 */ -114,239,239,240,114,241,241,241,241,241,241,241,241,241,114,241, -241,241,114,241,241,241,241,241,241,241,241,241,241,241,241,241, -241,241,241,241,241,241,241,241,241,114,241,241,241,241,241,241, -241,114,241,241,114,241,241,241,241,241,114,114,239,241,240,240, -240,239,239,239,239,239,114,239,239,240,114,240,240,239,114,114, -241,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -241,241,239,239,114,114,242,242,242,242,242,242,242,242,242,242, -243,244,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +115,240,240,241,115,242,242,242,242,242,242,242,242,242,115,242, +242,242,115,242,242,242,242,242,242,242,242,242,242,242,242,242, +242,242,242,242,242,242,242,242,242,115,242,242,242,242,242,242, +242,115,242,242,115,242,242,242,242,242,115,115,240,242,241,241, +241,240,240,240,240,240,115,240,240,241,115,241,241,240,115,115, +242,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +242,242,240,240,115,115,243,243,243,243,243,243,243,243,243,243, +244,245,115,115,115,115,115,115,115,242,115,115,115,115,115,115, /* block 22 */ -114,245,246,246,114,247,247,247,247,247,247,247,247,114,114,247, -247,114,114,247,247,247,247,247,247,247,247,247,247,247,247,247, -247,247,247,247,247,247,247,247,247,114,247,247,247,247,247,247, -247,114,247,247,114,247,247,247,247,247,114,114,245,247,248,245, -246,245,245,245,245,114,114,246,246,114,114,246,246,245,114,114, -114,114,114,114,114,114,245,248,114,114,114,114,247,247,114,247, -247,247,245,245,114,114,249,249,249,249,249,249,249,249,249,249, -250,247,251,251,251,251,251,251,114,114,114,114,114,114,114,114, +115,246,247,247,115,248,248,248,248,248,248,248,248,115,115,248, +248,115,115,248,248,248,248,248,248,248,248,248,248,248,248,248, +248,248,248,248,248,248,248,248,248,115,248,248,248,248,248,248, +248,115,248,248,115,248,248,248,248,248,115,115,246,248,249,246, +247,246,246,246,246,115,115,247,247,115,115,247,247,246,115,115, +115,115,115,115,115,115,246,249,115,115,115,115,248,248,115,248, +248,248,246,246,115,115,250,250,250,250,250,250,250,250,250,250, +251,248,252,252,252,252,252,252,115,115,115,115,115,115,115,115, /* block 23 */ -114,114,252,253,114,253,253,253,253,253,253,114,114,114,253,253, -253,114,253,253,253,253,114,114,114,253,253,114,253,114,253,253, -114,114,114,253,253,114,114,114,253,253,253,114,114,114,253,253, -253,253,253,253,253,253,253,253,253,253,114,114,114,114,254,255, -252,255,255,114,114,114,255,255,255,114,255,255,255,252,114,114, -253,114,114,114,114,114,114,254,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,256,256,256,256,256,256,256,256,256,256, -257,257,257,258,258,258,258,258,258,259,258,114,114,114,114,114, +115,115,253,254,115,254,254,254,254,254,254,115,115,115,254,254, +254,115,254,254,254,254,115,115,115,254,254,115,254,115,254,254, +115,115,115,254,254,115,115,115,254,254,254,115,115,115,254,254, +254,254,254,254,254,254,254,254,254,254,115,115,115,115,255,256, +253,256,256,115,115,115,256,256,256,115,256,256,256,253,115,115, +254,115,115,115,115,115,115,255,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,257,257,257,257,257,257,257,257,257,257, +258,258,258,259,259,259,259,259,259,260,259,115,115,115,115,115, /* block 24 */ -260,261,261,261,114,262,262,262,262,262,262,262,262,114,262,262, -262,114,262,262,262,262,262,262,262,262,262,262,262,262,262,262, -262,262,262,262,262,262,262,262,262,114,262,262,262,262,262,262, -262,262,262,262,262,262,262,262,262,262,114,114,114,262,260,260, -260,261,261,261,261,114,260,260,260,114,260,260,260,260,114,114, -114,114,114,114,114,260,260,114,262,262,114,114,114,114,114,114, -262,262,260,260,114,114,263,263,263,263,263,263,263,263,263,263, -114,114,114,114,114,114,114,114,264,264,264,264,264,264,264,265, +261,262,262,262,115,263,263,263,263,263,263,263,263,115,263,263, +263,115,263,263,263,263,263,263,263,263,263,263,263,263,263,263, +263,263,263,263,263,263,263,263,263,115,263,263,263,263,263,263, +263,263,263,263,263,263,263,263,263,263,115,115,115,263,261,261, +261,262,262,262,262,115,261,261,261,115,261,261,261,261,115,115, +115,115,115,115,115,261,261,115,263,263,263,115,115,115,115,115, +263,263,261,261,115,115,264,264,264,264,264,264,264,264,264,264, +115,115,115,115,115,115,115,115,265,265,265,265,265,265,265,266, /* block 25 */ -114,266,267,267,114,268,268,268,268,268,268,268,268,114,268,268, -268,114,268,268,268,268,268,268,268,268,268,268,268,268,268,268, -268,268,268,268,268,268,268,268,268,114,268,268,268,268,268,268, -268,268,268,268,114,268,268,268,268,268,114,114,266,268,267,266, -267,267,269,267,267,114,266,267,267,114,267,267,266,266,114,114, -114,114,114,114,114,269,269,114,114,114,114,114,114,114,268,114, -268,268,266,266,114,114,270,270,270,270,270,270,270,270,270,270, -114,268,268,114,114,114,114,114,114,114,114,114,114,114,114,114, +115,267,268,268,115,269,269,269,269,269,269,269,269,115,269,269, +269,115,269,269,269,269,269,269,269,269,269,269,269,269,269,269, +269,269,269,269,269,269,269,269,269,115,269,269,269,269,269,269, +269,269,269,269,115,269,269,269,269,269,115,115,267,269,268,267, +268,268,270,268,268,115,267,268,268,115,268,268,267,267,115,115, +115,115,115,115,115,270,270,115,115,115,115,115,115,115,269,115, +269,269,267,267,115,115,271,271,271,271,271,271,271,271,271,271, +115,269,269,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 26 */ -114,271,272,272,114,273,273,273,273,273,273,273,273,114,273,273, -273,114,273,273,273,273,273,273,273,273,273,273,273,273,273,273, -273,273,273,273,273,273,273,273,273,273,273,273,273,273,273,273, -273,273,273,273,273,273,273,273,273,273,273,114,114,273,274,272, -272,271,271,271,271,114,272,272,272,114,272,272,272,271,273,114, -114,114,114,114,114,114,114,274,114,114,114,114,114,114,114,114, -273,273,271,271,114,114,275,275,275,275,275,275,275,275,275,275, -276,276,276,276,276,276,114,114,114,277,273,273,273,273,273,273, +115,272,273,273,115,274,274,274,274,274,274,274,274,115,274,274, +274,115,274,274,274,274,274,274,274,274,274,274,274,274,274,274, +274,274,274,274,274,274,274,274,274,274,274,274,274,274,274,274, +274,274,274,274,274,274,274,274,274,274,274,115,115,274,275,273, +273,272,272,272,272,115,273,273,273,115,273,273,273,272,274,115, +115,115,115,115,115,115,115,275,115,115,115,115,115,115,115,274, +274,274,272,272,115,115,276,276,276,276,276,276,276,276,276,276, +277,277,277,277,277,277,115,115,115,278,274,274,274,274,274,274, /* block 27 */ -114,114,278,278,114,279,279,279,279,279,279,279,279,279,279,279, -279,279,279,279,279,279,279,114,114,114,279,279,279,279,279,279, -279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,279, -279,279,114,279,279,279,279,279,279,279,279,279,114,279,114,114, -279,279,279,279,279,279,279,114,114,114,280,114,114,114,114,281, -278,278,280,280,280,114,280,114,278,278,278,278,278,278,278,281, -114,114,114,114,114,114,282,282,282,282,282,282,282,282,282,282, -114,114,278,278,283,114,114,114,114,114,114,114,114,114,114,114, +115,115,279,279,115,280,280,280,280,280,280,280,280,280,280,280, +280,280,280,280,280,280,280,115,115,115,280,280,280,280,280,280, +280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280, +280,280,115,280,280,280,280,280,280,280,280,280,115,280,115,115, +280,280,280,280,280,280,280,115,115,115,281,115,115,115,115,282, +279,279,281,281,281,115,281,115,279,279,279,279,279,279,279,282, +115,115,115,115,115,115,283,283,283,283,283,283,283,283,283,283, +115,115,279,279,284,115,115,115,115,115,115,115,115,115,115,115, /* block 28 */ -114,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284, -284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284, -284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284, -284,285,284,286,285,285,285,285,285,285,285,114,114,114,114, 5, -284,284,284,284,284,284,287,285,285,285,285,285,285,285,285,288, -289,289,289,289,289,289,289,289,289,289,288,288,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +115,285,285,285,285,285,285,285,285,285,285,285,285,285,285,285, +285,285,285,285,285,285,285,285,285,285,285,285,285,285,285,285, +285,285,285,285,285,285,285,285,285,285,285,285,285,285,285,285, +285,286,285,287,286,286,286,286,286,286,286,115,115,115,115, 5, +285,285,285,285,285,285,288,286,286,286,286,286,286,286,286,289, +290,290,290,290,290,290,290,290,290,290,289,289,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 29 */ -114,290,290,114,290,114,114,290,290,114,290,114,114,290,114,114, -114,114,114,114,290,290,290,290,114,290,290,290,290,290,290,290, -114,290,290,290,114,290,114,290,114,114,290,290,114,290,290,290, -290,291,290,292,291,291,291,291,291,291,114,291,291,290,114,114, -290,290,290,290,290,114,293,114,291,291,291,291,291,291,114,114, -294,294,294,294,294,294,294,294,294,294,114,114,290,290,290,290, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +115,291,291,115,291,115,115,291,291,115,291,115,115,291,115,115, +115,115,115,115,291,291,291,291,115,291,291,291,291,291,291,291, +115,291,291,291,115,291,115,291,115,115,291,291,115,291,291,291, +291,292,291,293,292,292,292,292,292,292,115,292,292,291,115,115, +291,291,291,291,291,115,294,115,292,292,292,292,292,292,115,115, +295,295,295,295,295,295,295,295,295,295,115,115,291,291,291,291, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 30 */ -295,296,296,296,297,297,297,297,297,297,297,297,297,297,297,297, -297,297,297,296,297,296,296,296,298,298,296,296,296,296,296,296, -299,299,299,299,299,299,299,299,299,299,300,300,300,300,300,300, -300,300,300,300,296,298,296,298,296,298,301,302,301,302,303,303, -295,295,295,295,295,295,295,295,114,295,295,295,295,295,295,295, -295,295,295,295,295,295,295,295,295,295,295,295,295,295,295,295, -295,295,295,295,295,295,295,295,295,295,295,295,295,114,114,114, -114,298,298,298,298,298,298,298,298,298,298,298,298,298,298,303, +296,297,297,297,298,298,298,298,298,298,298,298,298,298,298,298, +298,298,298,297,298,297,297,297,299,299,297,297,297,297,297,297, +300,300,300,300,300,300,300,300,300,300,301,301,301,301,301,301, +301,301,301,301,297,299,297,299,297,299,302,303,302,303,304,304, +296,296,296,296,296,296,296,296,115,296,296,296,296,296,296,296, +296,296,296,296,296,296,296,296,296,296,296,296,296,296,296,296, +296,296,296,296,296,296,296,296,296,296,296,296,296,115,115,115, +115,299,299,299,299,299,299,299,299,299,299,299,299,299,299,304, /* block 31 */ -298,298,298,298,298,297,298,298,295,295,295,295,295,298,298,298, -298,298,298,298,298,298,298,298,114,298,298,298,298,298,298,298, -298,298,298,298,298,298,298,298,298,298,298,298,298,298,298,298, -298,298,298,298,298,298,298,298,298,298,298,298,298,114,296,296, -296,296,296,296,296,296,298,296,296,296,296,296,296,114,296,296, -297,297,297,297,297, 19, 19, 19, 19,297,297,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +299,299,299,299,299,298,299,299,296,296,296,296,296,299,299,299, +299,299,299,299,299,299,299,299,115,299,299,299,299,299,299,299, +299,299,299,299,299,299,299,299,299,299,299,299,299,299,299,299, +299,299,299,299,299,299,299,299,299,299,299,299,299,115,297,297, +297,297,297,297,297,297,299,297,297,297,297,297,297,115,297,297, +298,298,298,298,298, 19, 19, 19, 19,298,298,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 32 */ -304,304,304,304,304,304,304,304,304,304,304,304,304,304,304,304, -304,304,304,304,304,304,304,304,304,304,304,304,304,304,304,304, -304,304,304,304,304,304,304,304,304,304,304,305,305,306,306,306, -306,307,306,306,306,306,306,306,305,306,306,307,307,306,306,304, -308,308,308,308,308,308,308,308,308,308,309,309,309,309,309,309, -304,304,304,304,304,304,307,307,306,306,304,304,304,304,306,306, -306,304,305,305,305,304,304,305,305,305,305,305,305,305,304,304, -304,306,306,306,306,304,304,304,304,304,304,304,304,304,304,304, +305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305, +305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305, +305,305,305,305,305,305,305,305,305,305,305,306,306,307,307,307, +307,308,307,307,307,307,307,307,306,307,307,308,308,307,307,305, +309,309,309,309,309,309,309,309,309,309,310,310,310,310,310,310, +305,305,305,305,305,305,308,308,307,307,305,305,305,305,307,307, +307,305,306,306,306,305,305,306,306,306,306,306,306,306,305,305, +305,307,307,307,307,305,305,305,305,305,305,305,305,305,305,305, /* block 33 */ -304,304,306,305,307,306,306,305,305,305,305,305,305,306,304,305, -308,308,308,308,308,308,308,308,308,308,305,305,305,306,310,310, -311,311,311,311,311,311,311,311,311,311,311,311,311,311,311,311, -311,311,311,311,311,311,311,311,311,311,311,311,311,311,311,311, -311,311,311,311,311,311,114,311,114,114,114,114,114,311,114,114, +305,305,307,306,308,307,307,306,306,306,306,306,306,307,305,306, +309,309,309,309,309,309,309,309,309,309,306,306,306,307,311,311, 312,312,312,312,312,312,312,312,312,312,312,312,312,312,312,312, 312,312,312,312,312,312,312,312,312,312,312,312,312,312,312,312, -312,312,312,312,312,312,312,312,312,312,312, 4,313,312,312,312, +312,312,312,312,312,312,115,312,115,115,115,115,115,312,115,115, +313,313,313,313,313,313,313,313,313,313,313,313,313,313,313,313, +313,313,313,313,313,313,313,313,313,313,313,313,313,313,313,313, +313,313,313,313,313,313,313,313,313,313,313, 4,314,313,313,313, /* block 34 */ -314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, -314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, -314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, -314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, -314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, -314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, 315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, 315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +316,316,316,316,316,316,316,316,316,316,316,316,316,316,316,316, +316,316,316,316,316,316,316,316,316,316,316,316,316,316,316,316, /* block 35 */ -315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, -315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, -315,315,315,315,315,315,315,315,316,316,316,316,316,316,316,316, -316,316,316,316,316,316,316,316,316,316,316,316,316,316,316,316, -316,316,316,316,316,316,316,316,316,316,316,316,316,316,316,316, -316,316,316,316,316,316,316,316,316,316,316,316,316,316,316,316, 316,316,316,316,316,316,316,316,316,316,316,316,316,316,316,316, 316,316,316,316,316,316,316,316,316,316,316,316,316,316,316,316, +316,316,316,316,316,316,316,316,317,317,317,317,317,317,317,317, +317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317, +317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317, +317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317, +317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317, +317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317, /* block 36 */ -317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317, -317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317, -317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317, -317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317, -317,317,317,317,317,317,317,317,317,114,317,317,317,317,114,114, -317,317,317,317,317,317,317,114,317,114,317,317,317,317,114,114, -317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317, -317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317, +318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318, +318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318, +318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318, +318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318, +318,318,318,318,318,318,318,318,318,115,318,318,318,318,115,115, +318,318,318,318,318,318,318,115,318,115,318,318,318,318,115,115, +318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318, +318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318, /* block 37 */ -317,317,317,317,317,317,317,317,317,114,317,317,317,317,114,114, -317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317, -317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317, -317,114,317,317,317,317,114,114,317,317,317,317,317,317,317,114, -317,114,317,317,317,317,114,114,317,317,317,317,317,317,317,317, -317,317,317,317,317,317,317,114,317,317,317,317,317,317,317,317, -317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317, -317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317, +318,318,318,318,318,318,318,318,318,115,318,318,318,318,115,115, +318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318, +318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318, +318,115,318,318,318,318,115,115,318,318,318,318,318,318,318,115, +318,115,318,318,318,318,115,115,318,318,318,318,318,318,318,318, +318,318,318,318,318,318,318,115,318,318,318,318,318,318,318,318, +318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318, +318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318, /* block 38 */ -317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317, -317,114,317,317,317,317,114,114,317,317,317,317,317,317,317,317, -317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317, -317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317, -317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317, -317,317,317,317,317,317,317,317,317,317,317,114,114,318,318,318, -319,319,319,319,319,319,319,319,319,320,320,320,320,320,320,320, -320,320,320,320,320,320,320,320,320,320,320,320,320,114,114,114, +318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318, +318,115,318,318,318,318,115,115,318,318,318,318,318,318,318,318, +318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318, +318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318, +318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318, +318,318,318,318,318,318,318,318,318,318,318,115,115,319,319,319, +320,320,320,320,320,320,320,320,320,321,321,321,321,321,321,321, +321,321,321,321,321,321,321,321,321,321,321,321,321,115,115,115, /* block 39 */ -317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317, -321,321,321,321,321,321,321,321,321,321,114,114,114,114,114,114, -322,322,322,322,322,322,322,322,322,322,322,322,322,322,322,322, -322,322,322,322,322,322,322,322,322,322,322,322,322,322,322,322, -322,322,322,322,322,322,322,322,322,322,322,322,322,322,322,322, -322,322,322,322,322,322,322,322,322,322,322,322,322,322,322,322, -322,322,322,322,322,322,322,322,322,322,322,322,322,322,322,322, -322,322,322,322,322,114,114,114,114,114,114,114,114,114,114,114, +318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318, +322,322,322,322,322,322,322,322,322,322,115,115,115,115,115,115, +323,323,323,323,323,323,323,323,323,323,323,323,323,323,323,323, +323,323,323,323,323,323,323,323,323,323,323,323,323,323,323,323, +323,323,323,323,323,323,323,323,323,323,323,323,323,323,323,323, +323,323,323,323,323,323,323,323,323,323,323,323,323,323,323,323, +323,323,323,323,323,323,323,323,323,323,323,323,323,323,323,323, +324,324,324,324,324,324,115,115,325,325,325,325,325,325,115,115, /* block 40 */ -323,324,324,324,324,324,324,324,324,324,324,324,324,324,324,324, -324,324,324,324,324,324,324,324,324,324,324,324,324,324,324,324, -324,324,324,324,324,324,324,324,324,324,324,324,324,324,324,324, -324,324,324,324,324,324,324,324,324,324,324,324,324,324,324,324, -324,324,324,324,324,324,324,324,324,324,324,324,324,324,324,324, -324,324,324,324,324,324,324,324,324,324,324,324,324,324,324,324, -324,324,324,324,324,324,324,324,324,324,324,324,324,324,324,324, -324,324,324,324,324,324,324,324,324,324,324,324,324,324,324,324, +326,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, +327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, +327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, +327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, +327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, +327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, +327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, +327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, /* block 41 */ -324,324,324,324,324,324,324,324,324,324,324,324,324,324,324,324, -324,324,324,324,324,324,324,324,324,324,324,324,324,324,324,324, -324,324,324,324,324,324,324,324,324,324,324,324,324,324,324,324, -324,324,324,324,324,324,324,324,324,324,324,324,324,324,324,324, -324,324,324,324,324,324,324,324,324,324,324,324,324,324,324,324, -324,324,324,324,324,324,324,324,324,324,324,324,324,324,324,324, -324,324,324,324,324,324,324,324,324,324,324,324,324,324,324,324, -324,324,324,324,324,324,324,324,324,324,324,324,324,324,324,324, +327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, +327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, +327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, +327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, +327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, +327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, +327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, +327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, /* block 42 */ -324,324,324,324,324,324,324,324,324,324,324,324,324,324,324,324, -324,324,324,324,324,324,324,324,324,324,324,324,324,324,324,324, -324,324,324,324,324,324,324,324,324,324,324,324,324,324,324,324, -324,324,324,324,324,324,324,324,324,324,324,324,324,324,324,324, -324,324,324,324,324,324,324,324,324,324,324,324,324,324,324,324, -324,324,324,324,324,324,324,324,324,324,324,324,324,324,324,324, -324,324,324,324,324,324,324,324,324,324,324,324,324,325,325,324, -324,324,324,324,324,324,324,324,324,324,324,324,324,324,324,324, +327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, +327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, +327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, +327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, +327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, +327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, +327,327,327,327,327,327,327,327,327,327,327,327,327,328,328,327, +327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, /* block 43 */ -326,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, -327,327,327,327,327,327,327,327,327,327,327,328,329,114,114,114, -330,330,330,330,330,330,330,330,330,330,330,330,330,330,330,330, -330,330,330,330,330,330,330,330,330,330,330,330,330,330,330,330, -330,330,330,330,330,330,330,330,330,330,330,330,330,330,330,330, -330,330,330,330,330,330,330,330,330,330,330,330,330,330,330,330, -330,330,330,330,330,330,330,330,330,330,330, 4, 4, 4,331,331, -331,330,330,330,330,330,330,330,330,114,114,114,114,114,114,114, +329,330,330,330,330,330,330,330,330,330,330,330,330,330,330,330, +330,330,330,330,330,330,330,330,330,330,330,331,332,115,115,115, +333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333, +333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333, +333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333, +333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333, +333,333,333,333,333,333,333,333,333,333,333, 4, 4, 4,334,334, +334,333,333,333,333,333,333,333,333,115,115,115,115,115,115,115, /* block 44 */ -332,332,332,332,332,332,332,332,332,332,332,332,332,114,332,332, -332,332,333,333,333,114,114,114,114,114,114,114,114,114,114,114, -334,334,334,334,334,334,334,334,334,334,334,334,334,334,334,334, -334,334,335,335,335, 4, 4,114,114,114,114,114,114,114,114,114, -336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,336, -336,336,337,337,114,114,114,114,114,114,114,114,114,114,114,114, -338,338,338,338,338,338,338,338,338,338,338,338,338,114,338,338, -338,114,339,339,114,114,114,114,114,114,114,114,114,114,114,114, +335,335,335,335,335,335,335,335,335,335,335,335,335,115,335,335, +335,335,336,336,336,115,115,115,115,115,115,115,115,115,115,115, +337,337,337,337,337,337,337,337,337,337,337,337,337,337,337,337, +337,337,338,338,338, 4, 4,115,115,115,115,115,115,115,115,115, +339,339,339,339,339,339,339,339,339,339,339,339,339,339,339,339, +339,339,340,340,115,115,115,115,115,115,115,115,115,115,115,115, +341,341,341,341,341,341,341,341,341,341,341,341,341,115,341,341, +341,115,342,342,115,115,115,115,115,115,115,115,115,115,115,115, /* block 45 */ -340,340,340,340,340,340,340,340,340,340,340,340,340,340,340,340, -340,340,340,340,340,340,340,340,340,340,340,340,340,340,340,340, -340,340,340,340,340,340,340,340,340,340,340,340,340,340,340,340, -340,340,340,340,341,341,342,341,341,341,341,341,341,341,342,342, -342,342,342,342,342,342,341,342,342,341,341,341,341,341,341,341, -341,341,341,341,343,343,343,344,343,343,343,345,340,341,114,114, -346,346,346,346,346,346,346,346,346,346,114,114,114,114,114,114, -347,347,347,347,347,347,347,347,347,347,114,114,114,114,114,114, +343,343,343,343,343,343,343,343,343,343,343,343,343,343,343,343, +343,343,343,343,343,343,343,343,343,343,343,343,343,343,343,343, +343,343,343,343,343,343,343,343,343,343,343,343,343,343,343,343, +343,343,343,343,344,344,345,344,344,344,344,344,344,344,345,345, +345,345,345,345,345,345,344,345,345,344,344,344,344,344,344,344, +344,344,344,344,346,346,346,347,346,346,346,348,343,344,115,115, +349,349,349,349,349,349,349,349,349,349,115,115,115,115,115,115, +350,350,350,350,350,350,350,350,350,350,115,115,115,115,115,115, /* block 46 */ -348,348, 4, 4,348, 4,349,348,348,348,348,350,350,350,351,114, -352,352,352,352,352,352,352,352,352,352,114,114,114,114,114,114, -353,353,353,353,353,353,353,353,353,353,353,353,353,353,353,353, -353,353,353,353,353,353,353,353,353,353,353,353,353,353,353,353, -353,353,353,354,353,353,353,353,353,353,353,353,353,353,353,353, -353,353,353,353,353,353,353,353,353,353,353,353,353,353,353,353, -353,353,353,353,353,353,353,353,353,353,353,353,353,353,353,353, -353,353,353,353,353,353,353,353,114,114,114,114,114,114,114,114, +351,351, 4, 4,351, 4,352,351,351,351,351,353,353,353,354,115, +355,355,355,355,355,355,355,355,355,355,115,115,115,115,115,115, +356,356,356,356,356,356,356,356,356,356,356,356,356,356,356,356, +356,356,356,356,356,356,356,356,356,356,356,356,356,356,356,356, +356,356,356,357,356,356,356,356,356,356,356,356,356,356,356,356, +356,356,356,356,356,356,356,356,356,356,356,356,356,356,356,356, +356,356,356,356,356,356,356,356,356,356,356,356,356,356,356,356, +356,356,356,356,356,356,356,356,115,115,115,115,115,115,115,115, /* block 47 */ -353,353,353,353,353,353,353,353,353,353,353,353,353,353,353,353, -353,353,353,353,353,353,353,353,353,353,353,353,353,353,353,353, -353,353,353,353,353,353,353,353,353,350,353,114,114,114,114,114, -324,324,324,324,324,324,324,324,324,324,324,324,324,324,324,324, -324,324,324,324,324,324,324,324,324,324,324,324,324,324,324,324, -324,324,324,324,324,324,324,324,324,324,324,324,324,324,324,324, -324,324,324,324,324,324,324,324,324,324,324,324,324,324,324,324, -324,324,324,324,324,324,114,114,114,114,114,114,114,114,114,114, +356,356,356,356,356,356,356,356,356,356,356,356,356,356,356,356, +356,356,356,356,356,356,356,356,356,356,356,356,356,356,356,356, +356,356,356,356,356,356,356,356,356,353,356,115,115,115,115,115, +327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, +327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, +327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, +327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, +327,327,327,327,327,327,115,115,115,115,115,115,115,115,115,115, /* block 48 */ -355,355,355,355,355,355,355,355,355,355,355,355,355,355,355,355, -355,355,355,355,355,355,355,355,355,355,355,355,355,355,355,114, -356,356,356,357,357,357,357,356,356,357,357,357,114,114,114,114, -357,357,356,357,357,357,357,357,357,356,356,356,114,114,114,114, -358,114,114,114,359,359,360,360,360,360,360,360,360,360,360,360, -361,361,361,361,361,361,361,361,361,361,361,361,361,361,361,361, -361,361,361,361,361,361,361,361,361,361,361,361,361,361,114,114, -361,361,361,361,361,114,114,114,114,114,114,114,114,114,114,114, +358,358,358,358,358,358,358,358,358,358,358,358,358,358,358,358, +358,358,358,358,358,358,358,358,358,358,358,358,358,358,358,115, +359,359,359,360,360,360,360,359,359,360,360,360,115,115,115,115, +360,360,359,360,360,360,360,360,360,359,359,359,115,115,115,115, +361,115,115,115,362,362,363,363,363,363,363,363,363,363,363,363, +364,364,364,364,364,364,364,364,364,364,364,364,364,364,364,364, +364,364,364,364,364,364,364,364,364,364,364,364,364,364,115,115, +364,364,364,364,364,115,115,115,115,115,115,115,115,115,115,115, /* block 49 */ -362,362,362,362,362,362,362,362,362,362,362,362,362,362,362,362, -362,362,362,362,362,362,362,362,362,362,362,362,362,362,362,362, -362,362,362,362,362,362,362,362,362,362,362,362,114,114,114,114, -363,363,363,363,363,364,364,364,363,363,364,363,363,363,363,363, -363,362,362,362,362,362,362,362,363,363,114,114,114,114,114,114, -365,365,365,365,365,365,365,365,365,365,366,114,114,114,367,367, -368,368,368,368,368,368,368,368,368,368,368,368,368,368,368,368, -368,368,368,368,368,368,368,368,368,368,368,368,368,368,368,368, +365,365,365,365,365,365,365,365,365,365,365,365,365,365,365,365, +365,365,365,365,365,365,365,365,365,365,365,365,365,365,365,365, +365,365,365,365,365,365,365,365,365,365,365,365,115,115,115,115, +365,365,365,365,365,365,365,365,365,365,365,365,365,365,365,365, +365,365,365,365,365,365,365,365,365,365,115,115,115,115,115,115, +366,366,366,366,366,366,366,366,366,366,367,115,115,115,368,368, +369,369,369,369,369,369,369,369,369,369,369,369,369,369,369,369, +369,369,369,369,369,369,369,369,369,369,369,369,369,369,369,369, /* block 50 */ -369,369,369,369,369,369,369,369,369,369,369,369,369,369,369,369, -369,369,369,369,369,369,369,370,370,371,371,370,114,114,372,372, -373,373,373,373,373,373,373,373,373,373,373,373,373,373,373,373, -373,373,373,373,373,373,373,373,373,373,373,373,373,373,373,373, -373,373,373,373,373,373,373,373,373,373,373,373,373,373,373,373, -373,373,373,373,373,374,375,374,375,375,375,375,375,375,375,114, -375,376,375,376,376,375,375,375,375,375,375,375,375,374,374,374, -374,374,374,375,375,375,375,375,375,375,375,375,375,114,114,375, +370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, +370,370,370,370,370,370,370,371,371,372,372,371,115,115,373,373, +374,374,374,374,374,374,374,374,374,374,374,374,374,374,374,374, +374,374,374,374,374,374,374,374,374,374,374,374,374,374,374,374, +374,374,374,374,374,374,374,374,374,374,374,374,374,374,374,374, +374,374,374,374,374,375,376,375,376,376,376,376,376,376,376,115, +376,377,376,377,377,376,376,376,376,376,376,376,376,375,375,375, +375,375,375,376,376,376,376,376,376,376,376,376,376,115,115,376, /* block 51 */ -377,377,377,377,377,377,377,377,377,377,114,114,114,114,114,114, -377,377,377,377,377,377,377,377,377,377,114,114,114,114,114,114, -378,378,378,378,378,378,378,379,378,378,378,378,378,378,114,114, -109,109,109,109,109,109,109,109,109,109,109,109,109,109,380,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +378,378,378,378,378,378,378,378,378,378,115,115,115,115,115,115, +378,378,378,378,378,378,378,378,378,378,115,115,115,115,115,115, +379,379,379,379,379,379,379,380,379,379,379,379,379,379,115,115, +110,110,110,110,110,110,110,110,110,110,110,110,110,110,381,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 52 */ -381,381,381,381,382,383,383,383,383,383,383,383,383,383,383,383, -383,383,383,383,383,383,383,383,383,383,383,383,383,383,383,383, -383,383,383,383,383,383,383,383,383,383,383,383,383,383,383,383, -383,383,383,383,381,382,381,381,381,381,381,382,381,382,382,382, -382,382,381,382,382,383,383,383,383,383,383,383,114,114,114,114, -384,384,384,384,384,384,384,384,384,384,385,385,385,385,385,385, -385,386,386,386,386,386,386,386,386,386,386,381,381,381,381,381, -381,381,381,381,386,386,386,386,386,386,386,386,386,114,114,114, +382,382,382,382,383,384,384,384,384,384,384,384,384,384,384,384, +384,384,384,384,384,384,384,384,384,384,384,384,384,384,384,384, +384,384,384,384,384,384,384,384,384,384,384,384,384,384,384,384, +384,384,384,384,382,383,382,382,382,382,382,383,382,383,383,383, +383,383,382,383,383,384,384,384,384,384,384,384,115,115,115,115, +385,385,385,385,385,385,385,385,385,385,386,386,386,386,386,386, +386,387,387,387,387,387,387,387,387,387,387,382,382,382,382,382, +382,382,382,382,387,387,387,387,387,387,387,387,387,115,115,115, /* block 53 */ -387,387,388,389,389,389,389,389,389,389,389,389,389,389,389,389, -389,389,389,389,389,389,389,389,389,389,389,389,389,389,389,389, -389,388,387,387,387,387,388,388,387,387,388,387,387,387,389,389, -390,390,390,390,390,390,390,390,390,390,389,389,389,389,389,389, -391,391,391,391,391,391,391,391,391,391,391,391,391,391,391,391, -391,391,391,391,391,391,391,391,391,391,391,391,391,391,391,391, -391,391,391,391,391,391,392,393,392,392,393,393,393,392,393,392, -392,392,393,393,114,114,114,114,114,114,114,114,394,394,394,394, +388,388,389,390,390,390,390,390,390,390,390,390,390,390,390,390, +390,390,390,390,390,390,390,390,390,390,390,390,390,390,390,390, +390,389,388,388,388,388,389,389,388,388,389,388,388,388,390,390, +391,391,391,391,391,391,391,391,391,391,390,390,390,390,390,390, +392,392,392,392,392,392,392,392,392,392,392,392,392,392,392,392, +392,392,392,392,392,392,392,392,392,392,392,392,392,392,392,392, +392,392,392,392,392,392,393,394,393,393,394,394,394,393,394,393, +393,393,394,394,115,115,115,115,115,115,115,115,395,395,395,395, /* block 54 */ -395,395,395,395,395,395,395,395,395,395,395,395,395,395,395,395, -395,395,395,395,395,395,395,395,395,395,395,395,395,395,395,395, -395,395,395,395,396,396,396,396,396,396,396,396,397,397,397,397, -397,397,397,397,396,396,397,397,114,114,114,398,398,398,398,398, -399,399,399,399,399,399,399,399,399,399,114,114,114,395,395,395, -400,400,400,400,400,400,400,400,400,400,401,401,401,401,401,401, -401,401,401,401,401,401,401,401,401,401,401,401,401,401,401,401, -401,401,401,401,401,401,401,401,402,402,402,402,402,402,403,403, +396,396,396,396,396,396,396,396,396,396,396,396,396,396,396,396, +396,396,396,396,396,396,396,396,396,396,396,396,396,396,396,396, +396,396,396,396,397,397,397,397,397,397,397,397,398,398,398,398, +398,398,398,398,397,397,398,398,115,115,115,399,399,399,399,399, +400,400,400,400,400,400,400,400,400,400,115,115,115,396,396,396, +401,401,401,401,401,401,401,401,401,401,402,402,402,402,402,402, +402,402,402,402,402,402,402,402,402,402,402,402,402,402,402,402, +402,402,402,402,402,402,402,402,403,403,403,403,403,403,404,404, /* block 55 */ -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -404,404,404,404,404,404,404,404,114,114,114,114,114,114,114,114, -109,109,109, 4,109,109,109,109,109,109,109,109,109,109,109,109, -109,405,109,109,109,109,109,109,109,406,406,406,406,109,406,406, -406,406,405,405,109,406,406,114,109,109,114,114,114,114,114,114, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +405,405,405,405,405,405,405,405,115,115,115,115,115,115,115,115, +110,110,110, 4,110,110,110,110,110,110,110,110,110,110,110,110, +110,406,110,110,110,110,110,110,110,407,407,407,407,110,407,407, +407,407,406,406,110,407,407,115,110,110,115,115,115,115,115,115, /* block 56 */ 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, - 33, 33, 33, 33, 33, 33,122,122,122,122,122,407,106,106,106,106, -106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106, -106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106, -106,106,106,106,106,106,106,106,106,106,106,106,106,115,115,115, -115,115,106,106,106,106,115,115,115,115,115, 33, 33, 33, 33, 33, - 33, 33, 33, 33, 33, 33, 33, 33,408,409, 33, 33, 33,410, 33, 33, + 33, 33, 33, 33, 33, 33,123,123,123,123,123,408,107,107,107,107, +107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107, +107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107, +107,107,107,107,107,107,107,107,107,107,107,107,107,116,116,116, +116,116,107,107,107,107,116,116,116,116,116, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33,409,410, 33, 33, 33,411, 33, 33, /* block 57 */ 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, - 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,106,106,106,106,106, -106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106, -106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,115, -109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, -109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, -109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, -109,109,109,109,109,109,114,114,114,114,114,114,109,109,109,109, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,107,107,107,107,107, +107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107, +107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,116, +110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, +110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, +110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, +110,110,110,110,110,110,115,115,115,115,115,115,110,110,110,110, /* block 58 */ 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, @@ -1940,12 +1964,12 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 58112 bytes, block = 128 */ 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, -411,412, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, +412,413, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, /* block 59 */ 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, - 30, 31, 30, 31, 30, 31, 33, 33, 33, 33, 33,413, 33, 33,414, 33, + 30, 31, 30, 31, 30, 31, 33, 33, 33, 33, 33,414, 33, 33,415, 33, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, @@ -1954,57 +1978,57 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 58112 bytes, block = 128 */ 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, /* block 60 */ -415,415,415,415,415,415,415,415,416,416,416,416,416,416,416,416, -415,415,415,415,415,415,114,114,416,416,416,416,416,416,114,114, -415,415,415,415,415,415,415,415,416,416,416,416,416,416,416,416, -415,415,415,415,415,415,415,415,416,416,416,416,416,416,416,416, -415,415,415,415,415,415,114,114,416,416,416,416,416,416,114,114, -122,415,122,415,122,415,122,415,114,416,114,416,114,416,114,416, -415,415,415,415,415,415,415,415,416,416,416,416,416,416,416,416, -417,417,418,418,418,418,419,419,420,420,421,421,422,422,114,114, +416,416,416,416,416,416,416,416,417,417,417,417,417,417,417,417, +416,416,416,416,416,416,115,115,417,417,417,417,417,417,115,115, +416,416,416,416,416,416,416,416,417,417,417,417,417,417,417,417, +416,416,416,416,416,416,416,416,417,417,417,417,417,417,417,417, +416,416,416,416,416,416,115,115,417,417,417,417,417,417,115,115, +123,416,123,416,123,416,123,416,115,417,115,417,115,417,115,417, +416,416,416,416,416,416,416,416,417,417,417,417,417,417,417,417, +418,418,419,419,419,419,420,420,421,421,422,422,423,423,115,115, /* block 61 */ -415,415,415,415,415,415,415,415,423,423,423,423,423,423,423,423, -415,415,415,415,415,415,415,415,423,423,423,423,423,423,423,423, -415,415,415,415,415,415,415,415,423,423,423,423,423,423,423,423, -415,415,122,424,122,114,122,122,416,416,425,425,426,113,427,113, -113,113,122,424,122,114,122,122,428,428,428,428,426,113,113,113, -415,415,122,122,114,114,122,122,416,416,429,429,114,113,113,113, -415,415,122,122,122,163,122,122,416,416,430,430,168,113,113,113, -114,114,122,424,122,114,122,122,431,431,432,432,426,113,113,114, +416,416,416,416,416,416,416,416,424,424,424,424,424,424,424,424, +416,416,416,416,416,416,416,416,424,424,424,424,424,424,424,424, +416,416,416,416,416,416,416,416,424,424,424,424,424,424,424,424, +416,416,123,425,123,115,123,123,417,417,426,426,427,114,428,114, +114,114,123,425,123,115,123,123,429,429,429,429,427,114,114,114, +416,416,123,123,115,115,123,123,417,417,430,430,115,114,114,114, +416,416,123,123,123,164,123,123,417,417,431,431,169,114,114,114, +115,115,123,425,123,115,123,123,432,432,433,433,427,114,114,115, /* block 62 */ - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 22,433,433, 22, 22, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 22,434,434, 22, 22, 9, 9, 9, 9, 9, 9, 4, 4, 21, 25, 6, 21, 21, 25, 6, 21, - 4, 4, 4, 4, 4, 4, 4, 4,434,435, 22, 22, 22, 22, 22, 3, + 4, 4, 4, 4, 4, 4, 4, 4,435,436, 22, 22, 22, 22, 22, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 21, 25, 4, 4, 4, 4, 15, 15, 4, 4, 4, 8, 6, 7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 4, 15, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, - 22, 22, 22, 22, 22,436, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 23,106,114,114, 23, 23, 23, 23, 23, 23, 8, 8, 8, 6, 7,106, + 22, 22, 22, 22, 22,437, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 23,107,115,115, 23, 23, 23, 23, 23, 23, 8, 8, 8, 6, 7,107, /* block 63 */ - 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 8, 8, 8, 6, 7,114, -106,106,106,106,106,106,106,106,106,106,106,106,106,114,114,114, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 8, 8, 8, 6, 7,115, +107,107,107,107,107,107,107,107,107,107,107,107,107,115,115,115, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -109,109,109,109,109,109,109,109,109,109,109,109,109,380,380,380, -380,109,380,380,380,109,109,109,109,109,109,109,109,109,109,109, -109,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +110,110,110,110,110,110,110,110,110,110,110,110,110,381,381,381, +381,110,381,381,381,110,110,110,110,110,110,110,110,110,110,110, +110,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 64 */ - 19, 19,437, 19, 19, 19, 19,437, 19, 19,438,437,437,437,438,438, -437,437,437,438, 19,437, 19, 19, 8,437,437,437,437,437, 19, 19, - 19, 19, 19, 19,437, 19,439, 19,437, 19,440,441,437,437, 19,438, -437,437,442,437,438,406,406,406,406,438, 19, 19,438,438,437,437, - 8, 8, 8, 8, 8,437,438,438,438,438, 19, 8, 19, 19,443, 19, + 19, 19,438, 19, 19, 19, 19,438, 19, 19,439,438,438,438,439,439, +438,438,438,439, 19,438, 19, 19, 8,438,438,438,438,438, 19, 19, + 19, 19, 19, 19,438, 19,440, 19,438, 19,441,442,438,438, 19,439, +438,438,443,438,439,407,407,407,407,439, 19, 19,439,439,438,438, + 8, 8, 8, 8, 8,438,439,439,439,439, 19, 8, 19, 19,444, 19, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, -444,444,444,444,444,444,444,444,444,444,444,444,444,444,444,444, 445,445,445,445,445,445,445,445,445,445,445,445,445,445,445,445, +446,446,446,446,446,446,446,446,446,446,446,446,446,446,446,446, /* block 65 */ -446,446,446, 30, 31,446,446,446,446, 23,114,114,114,114,114,114, +447,447,447, 30, 31,447,447,447,447, 23, 19, 19,115,115,115,115, 8, 8, 8, 8, 8, 19, 19, 19, 19, 19, 8, 8, 19, 19, 19, 19, 8, 19, 19, 8, 19, 19, 8, 19, 19, 19, 19, 19, 19, 19, 8, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, @@ -2041,15 +2065,15 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 58112 bytes, block = 128 */ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 8, 8, 8, 8, 8, 8, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,114,114,114,114,114, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115,115, /* block 69 */ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, + 19, 19, 19, 19, 19, 19, 19,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, @@ -2057,10 +2081,10 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 58112 bytes, block = 128 */ 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19,447,447,447,447,447,447,447,447,447,447, -447,447,447,447,447,447,447,447,447,447,447,447,447,447,447,447, + 19, 19, 19, 19, 19, 19,448,448,448,448,448,448,448,448,448,448, 448,448,448,448,448,448,448,448,448,448,448,448,448,448,448,448, -448,448,448,448,448,448,448,448,448,448, 23, 23, 23, 23, 23, 23, +449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449, +449,449,449,449,449,449,449,449,449,449, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, /* block 71 */ @@ -2114,14 +2138,14 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 58112 bytes, block = 128 */ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, /* block 76 */ -449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449, -449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449, -449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449, -449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449, -449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449, -449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449, -449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449, -449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449, +450,450,450,450,450,450,450,450,450,450,450,450,450,450,450,450, +450,450,450,450,450,450,450,450,450,450,450,450,450,450,450,450, +450,450,450,450,450,450,450,450,450,450,450,450,450,450,450,450, +450,450,450,450,450,450,450,450,450,450,450,450,450,450,450,450, +450,450,450,450,450,450,450,450,450,450,450,450,450,450,450,450, +450,450,450,450,450,450,450,450,450,450,450,450,450,450,450,450, +450,450,450,450,450,450,450,450,450,450,450,450,450,450,450,450, +450,450,450,450,450,450,450,450,450,450,450,450,450,450,450,450, /* block 77 */ 8, 8, 8, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, @@ -2141,147 +2165,147 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 58112 bytes, block = 128 */ 8, 8, 8, 8, 8, 19, 19, 8, 8, 8, 8, 8, 8, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19,114,114, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19,115,115, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, /* block 79 */ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19,114,114, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19,115,115, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,114,114,114, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19,114, 19, 19, 19, 19, 19, 19, - 19, 19,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19,115, 19, 19, 19, 19, 19, 19, + 19, 19,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115, 19, 19, 19, 19, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 80 */ -450,450,450,450,450,450,450,450,450,450,450,450,450,450,450,450, -450,450,450,450,450,450,450,450,450,450,450,450,450,450,450,450, -450,450,450,450,450,450,450,450,450,450,450,450,450,450,450,114, 451,451,451,451,451,451,451,451,451,451,451,451,451,451,451,451, 451,451,451,451,451,451,451,451,451,451,451,451,451,451,451,451, -451,451,451,451,451,451,451,451,451,451,451,451,451,451,451,114, - 30, 31,452,453,454,455,456, 30, 31, 30, 31, 30, 31,457,458,459, -460, 33, 30, 31, 33, 30, 31, 33, 33, 33, 33, 33,106,106,461,461, +451,451,451,451,451,451,451,451,451,451,451,451,451,451,451,115, +452,452,452,452,452,452,452,452,452,452,452,452,452,452,452,452, +452,452,452,452,452,452,452,452,452,452,452,452,452,452,452,452, +452,452,452,452,452,452,452,452,452,452,452,452,452,452,452,115, + 30, 31,453,454,455,456,457, 30, 31, 30, 31, 30, 31,458,459,460, +461, 33, 30, 31, 33, 30, 31, 33, 33, 33, 33, 33,107,107,462,462, /* block 81 */ -159,160,159,160,159,160,159,160,159,160,159,160,159,160,159,160, -159,160,159,160,159,160,159,160,159,160,159,160,159,160,159,160, -159,160,159,160,159,160,159,160,159,160,159,160,159,160,159,160, -159,160,159,160,159,160,159,160,159,160,159,160,159,160,159,160, -159,160,159,160,159,160,159,160,159,160,159,160,159,160,159,160, -159,160,159,160,159,160,159,160,159,160,159,160,159,160,159,160, -159,160,159,160,462,463,463,463,463,463,463,159,160,159,160,464, -464,464,159,160,114,114,114,114,114,465,465,465,465,466,465,465, +160,161,160,161,160,161,160,161,160,161,160,161,160,161,160,161, +160,161,160,161,160,161,160,161,160,161,160,161,160,161,160,161, +160,161,160,161,160,161,160,161,160,161,160,161,160,161,160,161, +160,161,160,161,160,161,160,161,160,161,160,161,160,161,160,161, +160,161,160,161,160,161,160,161,160,161,160,161,160,161,160,161, +160,161,160,161,160,161,160,161,160,161,160,161,160,161,160,161, +160,161,160,161,463,464,464,464,464,464,464,160,161,160,161,465, +465,465,160,161,115,115,115,115,115,466,466,466,466,467,466,466, /* block 82 */ -467,467,467,467,467,467,467,467,467,467,467,467,467,467,467,467, -467,467,467,467,467,467,467,467,467,467,467,467,467,467,467,467, -467,467,467,467,467,467,114,467,114,114,114,114,114,467,114,114, 468,468,468,468,468,468,468,468,468,468,468,468,468,468,468,468, 468,468,468,468,468,468,468,468,468,468,468,468,468,468,468,468, -468,468,468,468,468,468,468,468,468,468,468,468,468,468,468,468, -468,468,468,468,468,468,468,468,114,114,114,114,114,114,114,469, -470,114,114,114,114,114,114,114,114,114,114,114,114,114,114,471, +468,468,468,468,468,468,115,468,115,115,115,115,115,468,115,115, +469,469,469,469,469,469,469,469,469,469,469,469,469,469,469,469, +469,469,469,469,469,469,469,469,469,469,469,469,469,469,469,469, +469,469,469,469,469,469,469,469,469,469,469,469,469,469,469,469, +469,469,469,469,469,469,469,469,115,115,115,115,115,115,115,470, +471,115,115,115,115,115,115,115,115,115,115,115,115,115,115,472, /* block 83 */ -317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317, -317,317,317,317,317,317,317,114,114,114,114,114,114,114,114,114, -317,317,317,317,317,317,317,114,317,317,317,317,317,317,317,114, -317,317,317,317,317,317,317,114,317,317,317,317,317,317,317,114, -317,317,317,317,317,317,317,114,317,317,317,317,317,317,317,114, -317,317,317,317,317,317,317,114,317,317,317,317,317,317,317,114, -177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177, -177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177, +318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318, +318,318,318,318,318,318,318,115,115,115,115,115,115,115,115,115, +318,318,318,318,318,318,318,115,318,318,318,318,318,318,318,115, +318,318,318,318,318,318,318,115,318,318,318,318,318,318,318,115, +318,318,318,318,318,318,318,115,318,318,318,318,318,318,318,115, +318,318,318,318,318,318,318,115,318,318,318,318,318,318,318,115, +178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178, +178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178, /* block 84 */ 4, 4, 21, 25, 21, 25, 4, 4, 4, 21, 25, 4, 21, 25, 4, 4, 4, 4, 4, 4, 4, 4, 4, 9, 4, 4, 9, 4, 21, 25, 4, 4, - 21, 25, 6, 7, 6, 7, 6, 7, 6, 7, 4, 4, 4, 4, 4,107, + 21, 25, 6, 7, 6, 7, 6, 7, 6, 7, 4, 4, 4, 4, 4,108, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 9, 9, 4, 4, 4, 4, - 9, 4, 6,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, + 9, 4, 6,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 85 */ -472,472,472,472,472,472,472,472,472,472,472,472,472,472,472,472, -472,472,472,472,472,472,472,472,472,472,114,472,472,472,472,472, -472,472,472,472,472,472,472,472,472,472,472,472,472,472,472,472, -472,472,472,472,472,472,472,472,472,472,472,472,472,472,472,472, -472,472,472,472,472,472,472,472,472,472,472,472,472,472,472,472, -472,472,472,472,472,472,472,472,472,472,472,472,472,472,472,472, -472,472,472,472,472,472,472,472,472,472,472,472,472,472,472,472, -472,472,472,472,114,114,114,114,114,114,114,114,114,114,114,114, +473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473, +473,473,473,473,473,473,473,473,473,473,115,473,473,473,473,473, +473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473, +473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473, +473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473, +473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473, +473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473, +473,473,473,473,115,115,115,115,115,115,115,115,115,115,115,115, /* block 86 */ -472,472,472,472,472,472,472,472,472,472,472,472,472,472,472,472, -472,472,472,472,472,472,472,472,472,472,472,472,472,472,472,472, -472,472,472,472,472,472,472,472,472,472,472,472,472,472,472,472, -472,472,472,472,472,472,472,472,472,472,472,472,472,472,472,472, -472,472,472,472,472,472,472,472,472,472,472,472,472,472,472,472, -472,472,472,472,472,472,472,472,472,472,472,472,472,472,472,472, -472,472,472,472,472,472,472,472,472,472,472,472,472,472,472,472, -472,472,472,472,472,472,472,472,472,472,472,472,472,472,472,472, +473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473, +473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473, +473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473, +473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473, +473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473, +473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473, +473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473, +473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473, /* block 87 */ -472,472,472,472,472,472,472,472,472,472,472,472,472,472,472,472, -472,472,472,472,472,472,472,472,472,472,472,472,472,472,472,472, -472,472,472,472,472,472,472,472,472,472,472,472,472,472,472,472, -472,472,472,472,472,472,472,472,472,472,472,472,472,472,472,472, -472,472,472,472,472,472,472,472,472,472,472,472,472,472,472,472, -472,472,472,472,472,472,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,114,114,114,114, +473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473, +473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473, +473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473, +473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473, +473,473,473,473,473,473,473,473,473,473,473,473,473,473,473,473, +473,473,473,473,473,473,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115, /* block 88 */ - 3, 4, 4, 4, 19,473,406,474, 6, 7, 6, 7, 6, 7, 6, 7, + 3, 4, 4, 4, 19,474,407,475, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 19, 19, 6, 7, 6, 7, 6, 7, 6, 7, 9, 6, 7, 7, - 19,474,474,474,474,474,474,474,474,474,109,109,109,109,475,475, - 9,107,107,107,107,107, 19, 19,474,474,474,473,406, 4, 19, 19, -114,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, -476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, -476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, -476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, + 19,475,475,475,475,475,475,475,475,475,110,110,110,110,476,476, + 9,108,108,108,108,108, 19, 19,475,475,475,474,407, 4, 19, 19, +115,477,477,477,477,477,477,477,477,477,477,477,477,477,477,477, +477,477,477,477,477,477,477,477,477,477,477,477,477,477,477,477, +477,477,477,477,477,477,477,477,477,477,477,477,477,477,477,477, +477,477,477,477,477,477,477,477,477,477,477,477,477,477,477,477, /* block 89 */ -476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, -476,476,476,476,476,476,476,114,114,109,109, 14, 14,477,477,476, - 9,478,478,478,478,478,478,478,478,478,478,478,478,478,478,478, -478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,478, -478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,478, -478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,478, -478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,478, -478,478,478,478,478,478,478,478,478,478,478, 4,107,479,479,478, +477,477,477,477,477,477,477,477,477,477,477,477,477,477,477,477, +477,477,477,477,477,477,477,115,115,110,110, 14, 14,478,478,477, + 9,479,479,479,479,479,479,479,479,479,479,479,479,479,479,479, +479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,479, +479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,479, +479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,479, +479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,479, +479,479,479,479,479,479,479,479,479,479,479, 4,108,480,480,479, /* block 90 */ -114,114,114,114,114,480,480,480,480,480,480,480,480,480,480,480, -480,480,480,480,480,480,480,480,480,480,480,480,480,480,480,480, -480,480,480,480,480,480,480,480,480,480,480,480,480,480,114,114, -114,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481, -481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481, -481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481, -481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481, +115,115,115,115,115,481,481,481,481,481,481,481,481,481,481,481, 481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481, +481,481,481,481,481,481,481,481,481,481,481,481,481,481,115,115, +115,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482, +482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482, +482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482, +482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482, +482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482, /* block 91 */ -481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,114, +482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,115, 19, 19, 23, 23, 23, 23, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, -480,480,480,480,480,480,480,480,480,480,480,480,480,480,480,480, -480,480,480,480,480,480,480,480,480,480,480,114,114,114,114,114, +481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481, +481,481,481,481,481,481,481,481,481,481,481,115,115,115,115,115, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19,114,114,114,114,114,114,114,114,114,114,114,114, -478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,478, + 19, 19, 19, 19,115,115,115,115,115,115,115,115,115,115,115,115, +479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,479, /* block 92 */ -482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482, -482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,114, +483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483, +483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,115, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 23, 23, 23, 23, 23, 23, 23, 23, 19, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, -482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482, -482,482,482,482,482,482,482,482,482,482,482,482,482,482,482, 19, +483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483, +483,483,483,483,483,483,483,483,483,483,483,483,483,483,483, 19, /* block 93 */ 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 19, 19, 19, 19, 19, 19, @@ -2289,1229 +2313,1229 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 58112 bytes, block = 128 */ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, -483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483, -483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483, -483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,114, +484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, +484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, +484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,115, /* block 94 */ -483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483, -483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483, -483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483, -483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483, -483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483, -483,483,483,483,483,483,483,483, 19, 19, 19, 19, 19, 19, 19, 19, +484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, +484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, +484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, +484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, +484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, +484,484,484,484,484,484,484,484, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, /* block 95 */ -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, /* block 96 */ -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,114,114,114,114,114,114,114,114,114,114, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,115,115,115,115,115,115,115,115,115,115, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, /* block 97 */ -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 98 */ -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,486,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486, +486,486,486,486,486,487,486,486,486,486,486,486,486,486,486,486, +486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486, +486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486, +486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486, +486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486, +486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486, +486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486, /* block 99 */ -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486, +486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486, +486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486, +486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486, +486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486, +486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486, +486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486, +486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486, /* block 100 */ -485,485,485,485,485,485,485,485,485,485,485,485,485,114,114,114, -487,487,487,487,487,487,487,487,487,487,487,487,487,487,487,487, -487,487,487,487,487,487,487,487,487,487,487,487,487,487,487,487, -487,487,487,487,487,487,487,487,487,487,487,487,487,487,487,487, -487,487,487,487,487,487,487,114,114,114,114,114,114,114,114,114, +486,486,486,486,486,486,486,486,486,486,486,486,486,115,115,115, 488,488,488,488,488,488,488,488,488,488,488,488,488,488,488,488, 488,488,488,488,488,488,488,488,488,488,488,488,488,488,488,488, -488,488,488,488,488,488,488,488,489,489,489,489,489,489,490,490, +488,488,488,488,488,488,488,488,488,488,488,488,488,488,488,488, +488,488,488,488,488,488,488,115,115,115,115,115,115,115,115,115, +489,489,489,489,489,489,489,489,489,489,489,489,489,489,489,489, +489,489,489,489,489,489,489,489,489,489,489,489,489,489,489,489, +489,489,489,489,489,489,489,489,490,490,490,490,490,490,491,491, /* block 101 */ -491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,491, -491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,491, -491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,491, -491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,491, -491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,491, -491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,491, -491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,491, -491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,491, +492,492,492,492,492,492,492,492,492,492,492,492,492,492,492,492, +492,492,492,492,492,492,492,492,492,492,492,492,492,492,492,492, +492,492,492,492,492,492,492,492,492,492,492,492,492,492,492,492, +492,492,492,492,492,492,492,492,492,492,492,492,492,492,492,492, +492,492,492,492,492,492,492,492,492,492,492,492,492,492,492,492, +492,492,492,492,492,492,492,492,492,492,492,492,492,492,492,492, +492,492,492,492,492,492,492,492,492,492,492,492,492,492,492,492, +492,492,492,492,492,492,492,492,492,492,492,492,492,492,492,492, /* block 102 */ -491,491,491,491,491,491,491,491,491,491,491,491,492,493,493,493, -491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,491, -494,494,494,494,494,494,494,494,494,494,491,491,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -174,175,174,175,174,175,174,175,174,175,174,175,174,175,174,175, -174,175,174,175,174,175,174,175,174,175,174,175,174,175,174,175, -174,175,174,175,174,175,174,175,174,175,174,175,174,175,495,177, -178,178,178,496,177,177,177,177,177,177,177,177,177,177,496,408, +492,492,492,492,492,492,492,492,492,492,492,492,493,494,494,494, +492,492,492,492,492,492,492,492,492,492,492,492,492,492,492,492, +495,495,495,495,495,495,495,495,495,495,492,492,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +175,176,175,176,175,176,175,176,175,176,175,176,175,176,175,176, +175,176,175,176,175,176,175,176,175,176,175,176,175,176,175,176, +175,176,175,176,175,176,175,176,175,176,175,176,175,176,496,178, +179,179,179,497,178,178,178,178,178,178,178,178,178,178,497,409, /* block 103 */ -174,175,174,175,174,175,174,175,174,175,174,175,174,175,174,175, -174,175,174,175,174,175,174,175,174,175,174,175,408,408,114,177, -497,497,497,497,497,497,497,497,497,497,497,497,497,497,497,497, -497,497,497,497,497,497,497,497,497,497,497,497,497,497,497,497, -497,497,497,497,497,497,497,497,497,497,497,497,497,497,497,497, -497,497,497,497,497,497,497,497,497,497,497,497,497,497,497,497, -497,497,497,497,497,497,498,498,498,498,498,498,498,498,498,498, -499,499,500,500,500,500,500,500,114,114,114,114,114,114,114,114, +175,176,175,176,175,176,175,176,175,176,175,176,175,176,175,176, +175,176,175,176,175,176,175,176,175,176,175,176,409,409,178,178, +498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498, +498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498, +498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498, +498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498, +498,498,498,498,498,498,499,499,499,499,499,499,499,499,499,499, +500,500,501,501,501,501,501,501,115,115,115,115,115,115,115,115, /* block 104 */ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 14, 14,107,107,107,107,107,107,107,107,107, + 14, 14, 14, 14, 14, 14, 14,108,108,108,108,108,108,108,108,108, 14, 14, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 33, 33, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, -106, 33, 33, 33, 33, 33, 33, 33, 33, 30, 31, 30, 31,501, 30, 31, +107, 33, 33, 33, 33, 33, 33, 33, 33, 30, 31, 30, 31,502, 30, 31, /* block 105 */ - 30, 31, 30, 31, 30, 31, 30, 31,107, 14, 14, 30, 31,502, 33,114, + 30, 31, 30, 31, 30, 31, 30, 31,108, 14, 14, 30, 31,503, 33, 20, 30, 31, 30, 31, 33, 33, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, - 30, 31, 30, 31, 30, 31, 30, 31, 30, 31,503,504,505,506,114,114, -507,508,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114, 20,106,106, 33, 20, 20, 20, 20, 20, + 30, 31, 30, 31, 30, 31, 30, 31, 30, 31,504,505,506,507,115,115, +508,509,510,511, 30, 31, 30, 31,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115, 20,107,107, 33, 20, 20, 20, 20, 20, /* block 106 */ -509,509,510,509,509,509,510,509,509,509,509,510,509,509,509,509, -509,509,509,509,509,509,509,509,509,509,509,509,509,509,509,509, -509,509,509,511,511,510,510,511,512,512,512,512,114,114,114,114, - 23, 23, 23, 23, 23, 23, 19, 19, 5, 19,114,114,114,114,114,114, -513,513,513,513,513,513,513,513,513,513,513,513,513,513,513,513, -513,513,513,513,513,513,513,513,513,513,513,513,513,513,513,513, -513,513,513,513,513,513,513,513,513,513,513,513,513,513,513,513, -513,513,513,513,514,514,514,514,114,114,114,114,114,114,114,114, +512,512,513,512,512,512,513,512,512,512,512,513,512,512,512,512, +512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512, +512,512,512,514,514,513,513,514,515,515,515,515,115,115,115,115, + 23, 23, 23, 23, 23, 23, 19, 19, 5, 19,115,115,115,115,115,115, +516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516, +516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516, +516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516, +516,516,516,516,517,517,517,517,115,115,115,115,115,115,115,115, /* block 107 */ -515,515,516,516,516,516,516,516,516,516,516,516,516,516,516,516, -516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516, -516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516, -516,516,516,516,515,515,515,515,515,515,515,515,515,515,515,515, -515,515,515,515,517,114,114,114,114,114,114,114,114,114,518,518, -519,519,519,519,519,519,519,519,519,519,114,114,114,114,114,114, -221,221,221,221,221,221,221,221,221,221,221,221,221,221,221,221, -221,221,223,223,223,223,223,223,225,225,225,223,114,114,114,114, +518,518,519,519,519,519,519,519,519,519,519,519,519,519,519,519, +519,519,519,519,519,519,519,519,519,519,519,519,519,519,519,519, +519,519,519,519,519,519,519,519,519,519,519,519,519,519,519,519, +519,519,519,519,518,518,518,518,518,518,518,518,518,518,518,518, +518,518,518,518,520,115,115,115,115,115,115,115,115,115,521,521, +522,522,522,522,522,522,522,522,522,522,115,115,115,115,115,115, +222,222,222,222,222,222,222,222,222,222,222,222,222,222,222,222, +222,222,224,224,224,224,224,224,226,226,226,224,226,224,115,115, /* block 108 */ -520,520,520,520,520,520,520,520,520,520,521,521,521,521,521,521, -521,521,521,521,521,521,521,521,521,521,521,521,521,521,521,521, -521,521,521,521,521,521,522,522,522,522,522,522,522,522, 4,523, +523,523,523,523,523,523,523,523,523,523,524,524,524,524,524,524, 524,524,524,524,524,524,524,524,524,524,524,524,524,524,524,524, -524,524,524,524,524,524,524,525,525,525,525,525,525,525,525,525, -525,525,526,526,114,114,114,114,114,114,114,114,114,114,114,527, -314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, -314,314,314,314,314,314,314,314,314,314,314,314,314,114,114,114, +524,524,524,524,524,524,525,525,525,525,525,525,525,525, 4,526, +527,527,527,527,527,527,527,527,527,527,527,527,527,527,527,527, +527,527,527,527,527,527,527,528,528,528,528,528,528,528,528,528, +528,528,529,529,115,115,115,115,115,115,115,115,115,115,115,530, +315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, +315,315,315,315,315,315,315,315,315,315,315,315,315,115,115,115, /* block 109 */ -528,528,528,529,530,530,530,530,530,530,530,530,530,530,530,530, -530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,530, -530,530,530,530,530,530,530,530,530,530,530,530,530,530,530,530, -530,530,530,528,529,529,528,528,528,528,529,529,528,529,529,529, -529,531,531,531,531,531,531,531,531,531,531,531,531,531,114,107, -532,532,532,532,532,532,532,532,532,532,114,114,114,114,531,531, -304,304,304,304,304,306,533,304,304,304,304,304,304,304,304,304, -308,308,308,308,308,308,308,308,308,308,304,304,304,304,304,114, +531,531,531,532,533,533,533,533,533,533,533,533,533,533,533,533, +533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,533, +533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,533, +533,533,533,531,532,532,531,531,531,531,532,532,531,532,532,532, +532,534,534,534,534,534,534,534,534,534,534,534,534,534,115,108, +535,535,535,535,535,535,535,535,535,535,115,115,115,115,534,534, +305,305,305,305,305,307,536,305,305,305,305,305,305,305,305,305, +309,309,309,309,309,309,309,309,309,309,305,305,305,305,305,115, /* block 110 */ -534,534,534,534,534,534,534,534,534,534,534,534,534,534,534,534, -534,534,534,534,534,534,534,534,534,534,534,534,534,534,534,534, -534,534,534,534,534,534,534,534,534,535,535,535,535,535,535,536, -536,535,535,536,536,535,535,114,114,114,114,114,114,114,114,114, -534,534,534,535,534,534,534,534,534,534,534,534,535,536,114,114, -537,537,537,537,537,537,537,537,537,537,114,114,538,538,538,538, -304,304,304,304,304,304,304,304,304,304,304,304,304,304,304,304, -533,304,304,304,304,304,304,310,310,310,304,305,306,305,304,304, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,538,538,538,538,538,538,539, +539,538,538,539,539,538,538,115,115,115,115,115,115,115,115,115, +537,537,537,538,537,537,537,537,537,537,537,537,538,539,115,115, +540,540,540,540,540,540,540,540,540,540,115,115,541,541,541,541, +305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305, +536,305,305,305,305,305,305,311,311,311,305,306,307,306,305,305, /* block 111 */ -539,539,539,539,539,539,539,539,539,539,539,539,539,539,539,539, -539,539,539,539,539,539,539,539,539,539,539,539,539,539,539,539, -539,539,539,539,539,539,539,539,539,539,539,539,539,539,539,539, -540,539,540,540,540,539,539,540,540,539,539,539,539,539,540,540, -539,540,539,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,539,539,541,542,542, -543,543,543,543,543,543,543,543,543,543,543,544,545,545,544,544, -546,546,543,547,547,544,545,114,114,114,114,114,114,114,114,114, +542,542,542,542,542,542,542,542,542,542,542,542,542,542,542,542, +542,542,542,542,542,542,542,542,542,542,542,542,542,542,542,542, +542,542,542,542,542,542,542,542,542,542,542,542,542,542,542,542, +543,542,543,543,543,542,542,543,543,542,542,542,542,542,543,543, +542,543,542,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,542,542,544,545,545, +546,546,546,546,546,546,546,546,546,546,546,547,548,548,547,547, +549,549,546,550,550,547,548,115,115,115,115,115,115,115,115,115, /* block 112 */ -114,317,317,317,317,317,317,114,114,317,317,317,317,317,317,114, -114,317,317,317,317,317,317,114,114,114,114,114,114,114,114,114, -317,317,317,317,317,317,317,114,317,317,317,317,317,317,317,114, +115,318,318,318,318,318,318,115,115,318,318,318,318,318,318,115, +115,318,318,318,318,318,318,115,115,115,115,115,115,115,115,115, +318,318,318,318,318,318,318,115,318,318,318,318,318,318,318,115, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, - 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 14,106,106,106,106, -114,114,114,114, 33,122,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, + 33, 33, 33,551, 33, 33, 33, 33, 33, 33, 33, 14,107,107,107,107, + 33, 33, 33, 33, 33,123,115,115,115,115,115,115,115,115,115,115, +552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552, /* block 113 */ -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -543,543,543,543,543,543,543,543,543,543,543,543,543,543,543,543, -543,543,543,543,543,543,543,543,543,543,543,543,543,543,543,543, -543,543,543,544,544,545,544,544,545,544,544,546,544,545,114,114, -548,548,548,548,548,548,548,548,548,548,114,114,114,114,114,114, +552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552, +552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552, +552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552, +552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552, +546,546,546,546,546,546,546,546,546,546,546,546,546,546,546,546, +546,546,546,546,546,546,546,546,546,546,546,546,546,546,546,546, +546,546,546,547,547,548,547,547,548,547,547,549,547,548,115,115, +553,553,553,553,553,553,553,553,553,553,115,115,115,115,115,115, /* block 114 */ -549,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, -550,550,550,550,550,550,550,550,550,550,550,550,549,550,550,550, -550,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, -550,550,550,550,550,550,550,550,549,550,550,550,550,550,550,550, -550,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, -550,550,550,550,549,550,550,550,550,550,550,550,550,550,550,550, -550,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, -549,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, +554,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,554,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,554,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,554,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +554,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, /* block 115 */ -550,550,550,550,550,550,550,550,550,550,550,550,549,550,550,550, -550,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, -550,550,550,550,550,550,550,550,549,550,550,550,550,550,550,550, -550,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, -550,550,550,550,549,550,550,550,550,550,550,550,550,550,550,550, -550,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, -549,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, -550,550,550,550,550,550,550,550,550,550,550,550,549,550,550,550, +555,555,555,555,555,555,555,555,555,555,555,555,554,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,554,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,554,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +554,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,554,555,555,555, /* block 116 */ -550,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, -550,550,550,550,550,550,550,550,549,550,550,550,550,550,550,550, -550,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, -550,550,550,550,549,550,550,550,550,550,550,550,550,550,550,550, -550,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, -549,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, -550,550,550,550,550,550,550,550,550,550,550,550,549,550,550,550, -550,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, +555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,554,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,554,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +554,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,554,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, /* block 117 */ -550,550,550,550,550,550,550,550,549,550,550,550,550,550,550,550, -550,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, -550,550,550,550,549,550,550,550,550,550,550,550,550,550,550,550, -550,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, -549,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, -550,550,550,550,550,550,550,550,550,550,550,550,549,550,550,550, -550,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, -550,550,550,550,550,550,550,550,549,550,550,550,550,550,550,550, +555,555,555,555,555,555,555,555,554,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,554,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +554,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,554,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,554,555,555,555,555,555,555,555, /* block 118 */ -550,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, -550,550,550,550,549,550,550,550,550,550,550,550,550,550,550,550, -550,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, -549,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, -550,550,550,550,550,550,550,550,550,550,550,550,549,550,550,550, -550,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, -550,550,550,550,550,550,550,550,549,550,550,550,550,550,550,550, -550,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, +555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,554,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +554,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,554,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,554,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, /* block 119 */ -550,550,550,550,549,550,550,550,550,550,550,550,550,550,550,550, -550,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, -549,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, -550,550,550,550,550,550,550,550,550,550,550,550,549,550,550,550, -550,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, -550,550,550,550,550,550,550,550,549,550,550,550,550,550,550,550, -550,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, -550,550,550,550,549,550,550,550,550,550,550,550,550,550,550,550, +555,555,555,555,554,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +554,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,554,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,554,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,554,555,555,555,555,555,555,555,555,555,555,555, /* block 120 */ -550,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, -549,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, -550,550,550,550,550,550,550,550,550,550,550,550,549,550,550,550, -550,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, -550,550,550,550,550,550,550,550,549,550,550,550,550,550,550,550, -550,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, -550,550,550,550,549,550,550,550,550,550,550,550,550,550,550,550, -550,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, +555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +554,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,554,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,554,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,554,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, /* block 121 */ -550,550,550,550,550,550,550,550,549,550,550,550,550,550,550,550, -550,550,550,550,550,550,550,550,550,550,550,550,550,550,550,550, -550,550,550,550,114,114,114,114,114,114,114,114,114,114,114,114, -315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315, -315,315,315,315,315,315,315,114,114,114,114,316,316,316,316,316, +555,555,555,555,555,555,555,555,554,555,555,555,555,555,555,555, +555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555, +555,555,555,555,115,115,115,115,115,115,115,115,115,115,115,115, 316,316,316,316,316,316,316,316,316,316,316,316,316,316,316,316, -316,316,316,316,316,316,316,316,316,316,316,316,316,316,316,316, -316,316,316,316,316,316,316,316,316,316,316,316,114,114,114,114, +316,316,316,316,316,316,316,115,115,115,115,317,317,317,317,317, +317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317, +317,317,317,317,317,317,317,317,317,317,317,317,317,317,317,317, +317,317,317,317,317,317,317,317,317,317,317,317,115,115,115,115, /* block 122 */ -551,551,551,551,551,551,551,551,551,551,551,551,551,551,551,551, -551,551,551,551,551,551,551,551,551,551,551,551,551,551,551,551, -551,551,551,551,551,551,551,551,551,551,551,551,551,551,551,551, -551,551,551,551,551,551,551,551,551,551,551,551,551,551,551,551, -551,551,551,551,551,551,551,551,551,551,551,551,551,551,551,551, -551,551,551,551,551,551,551,551,551,551,551,551,551,551,551,551, -551,551,551,551,551,551,551,551,551,551,551,551,551,551,551,551, -551,551,551,551,551,551,551,551,551,551,551,551,551,551,551,551, +556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,556, +556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,556, +556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,556, +556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,556, +556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,556, +556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,556, +556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,556, +556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,556, /* block 123 */ -552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552, -552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552, -552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552, -552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552, -552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552, -552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552, -552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552, -552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552, +557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557, +557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557, +557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557, +557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557, +557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557, +557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557, +557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557, +557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557, /* block 124 */ -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,114,114, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,115,115, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, /* block 125 */ -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 126 */ - 33, 33, 33, 33, 33, 33, 33,114,114,114,114,114,114,114,114,114, -114,114,114,185,185,185,185,185,114,114,114,114,114,192,189,192, -192,192,192,192,192,192,192,192,192,553,192,192,192,192,192,192, -192,192,192,192,192,192,192,114,192,192,192,192,192,114,192,114, -192,192,114,192,192,114,192,192,192,192,192,192,192,192,192,192, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, + 33, 33, 33, 33, 33, 33, 33,115,115,115,115,115,115,115,115,115, +115,115,115,186,186,186,186,186,115,115,115,115,115,193,190,193, +193,193,193,193,193,193,193,193,193,558,193,193,193,193,193,193, +193,193,193,193,193,193,193,115,193,193,193,193,193,115,193,115, +193,193,115,193,193,115,193,193,193,193,193,193,193,193,193,193, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, /* block 127 */ -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,554,554,554,554,554,554,554,554,554,554,554,554,554,554, -554,554,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,559,559,559,559,559,559,559,559,559,559,559,559,559,559, +559,559,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, /* block 128 */ -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, /* block 129 */ -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199, 7, 6, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200, 7, 6, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, /* block 130 */ -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -114,114,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -199,199,199,199,199,199,199,199,199,199,199,199,196,197,114,114, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +115,115,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +200,200,200,200,200,200,200,200,200,200,200,200,197,198,115,115, /* block 131 */ -109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, - 4, 4, 4, 4, 4, 4, 4, 6, 7, 4,114,114,114,114,114,114, -109,109,109,109,109,109,109,109,109,109,109,109,109,109,114,114, +110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, + 4, 4, 4, 4, 4, 4, 4, 6, 7, 4,115,115,115,115,115,115, +110,110,110,110,110,110,110,110,110,110,110,110,110,110,178,178, 4, 9, 9, 15, 15, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 4, 4, 6, 7, 4, 4, 4, 4, 15, 15, 15, - 4, 4, 4,114, 4, 4, 4, 4, 9, 6, 7, 6, 7, 6, 7, 4, - 4, 4, 8, 9, 8, 8, 8,114, 4, 5, 4, 4,114,114,114,114, -199,199,199,199,199,114,199,199,199,199,199,199,199,199,199,199, + 4, 4, 4,115, 4, 4, 4, 4, 9, 6, 7, 6, 7, 6, 7, 4, + 4, 4, 8, 9, 8, 8, 8,115, 4, 5, 4, 4,115,115,115,115, +200,200,200,200,200,115,200,200,200,200,200,200,200,200,200,200, /* block 132 */ -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,114,114, 22, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,115,115, 22, /* block 133 */ -114, 4, 4, 4, 5, 4, 4, 4, 6, 7, 4, 8, 4, 9, 4, 4, +115, 4, 4, 4, 5, 4, 4, 4, 6, 7, 4, 8, 4, 9, 4, 4, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 4, 4, 8, 8, 8, 4, 4, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 6, 4, 7, 14, 15, 14, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 6, 8, 7, 8, 6, - 7, 4, 6, 7, 4, 4,478,478,478,478,478,478,478,478,478,478, -107,478,478,478,478,478,478,478,478,478,478,478,478,478,478,478, + 7, 4, 6, 7, 4, 4,479,479,479,479,479,479,479,479,479,479, +108,479,479,479,479,479,479,479,479,479,479,479,479,479,479,479, /* block 134 */ -478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,478, -478,478,478,478,478,478,478,478,478,478,478,478,478,478,555,555, -481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481, -481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,114, -114,114,481,481,481,481,481,481,114,114,481,481,481,481,481,481, -114,114,481,481,481,481,481,481,114,114,481,481,481,114,114,114, - 5, 5, 8, 14, 19, 5, 5,114, 19, 8, 8, 8, 8, 19, 19,114, -436,436,436,436,436,436,436,436,436, 22, 22, 22, 19, 19,114,114, +479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,479, +479,479,479,479,479,479,479,479,479,479,479,479,479,479,560,560, +482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482, +482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,115, +115,115,482,482,482,482,482,482,115,115,482,482,482,482,482,482, +115,115,482,482,482,482,482,482,115,115,482,482,482,115,115,115, + 5, 5, 8, 14, 19, 5, 5,115, 19, 8, 8, 8, 8, 19, 19,115, +437,437,437,437,437,437,437,437,437, 22, 22, 22, 19, 19,115,115, /* block 135 */ -556,556,556,556,556,556,556,556,556,556,556,556,114,556,556,556, -556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,556, -556,556,556,556,556,556,556,114,556,556,556,556,556,556,556,556, -556,556,556,556,556,556,556,556,556,556,556,114,556,556,114,556, -556,556,556,556,556,556,556,556,556,556,556,556,556,556,114,114, -556,556,556,556,556,556,556,556,556,556,556,556,556,556,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +561,561,561,561,561,561,561,561,561,561,561,561,115,561,561,561, +561,561,561,561,561,561,561,561,561,561,561,561,561,561,561,561, +561,561,561,561,561,561,561,115,561,561,561,561,561,561,561,561, +561,561,561,561,561,561,561,561,561,561,561,115,561,561,115,561, +561,561,561,561,561,561,561,561,561,561,561,561,561,561,115,115, +561,561,561,561,561,561,561,561,561,561,561,561,561,561,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 136 */ -556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,556, -556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,556, -556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,556, -556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,556, -556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,556, -556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,556, -556,556,556,556,556,556,556,556,556,556,556,556,556,556,556,556, -556,556,556,556,556,556,556,556,556,556,556,114,114,114,114,114, +561,561,561,561,561,561,561,561,561,561,561,561,561,561,561,561, +561,561,561,561,561,561,561,561,561,561,561,561,561,561,561,561, +561,561,561,561,561,561,561,561,561,561,561,561,561,561,561,561, +561,561,561,561,561,561,561,561,561,561,561,561,561,561,561,561, +561,561,561,561,561,561,561,561,561,561,561,561,561,561,561,561, +561,561,561,561,561,561,561,561,561,561,561,561,561,561,561,561, +561,561,561,561,561,561,561,561,561,561,561,561,561,561,561,561, +561,561,561,561,561,561,561,561,561,561,561,115,115,115,115,115, /* block 137 */ - 4, 4, 4,114,114,114,114, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 4, 4, 4,115,115,115,115, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, - 23, 23, 23, 23,114,114,114, 19, 19, 19, 19, 19, 19, 19, 19, 19, -557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557, -557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557, -557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557, -557,557,557,557,557,558,558,558,558,559,559,559,559,559,559,559, + 23, 23, 23, 23,115,115,115, 19, 19, 19, 19, 19, 19, 19, 19, 19, +562,562,562,562,562,562,562,562,562,562,562,562,562,562,562,562, +562,562,562,562,562,562,562,562,562,562,562,562,562,562,562,562, +562,562,562,562,562,562,562,562,562,562,562,562,562,562,562,562, +562,562,562,562,562,563,563,563,563,564,564,564,564,564,564,564, /* block 138 */ -559,559,559,559,559,559,559,559,559,559,558,558,559,114,114,114, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,114,114,114,114, -559,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +564,564,564,564,564,564,564,564,564,564,563,563,564,115,115,115, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115, +564,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,109,114,114, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,110,115,115, /* block 139 */ -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 140 */ -560,560,560,560,560,560,560,560,560,560,560,560,560,560,560,560, -560,560,560,560,560,560,560,560,560,560,560,560,560,114,114,114, -561,561,561,561,561,561,561,561,561,561,561,561,561,561,561,561, -561,561,561,561,561,561,561,561,561,561,561,561,561,561,561,561, -561,561,561,561,561,561,561,561,561,561,561,561,561,561,561,561, -561,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -109, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, - 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,114,114,114,114, +565,565,565,565,565,565,565,565,565,565,565,565,565,565,565,565, +565,565,565,565,565,565,565,565,565,565,565,565,565,115,115,115, +566,566,566,566,566,566,566,566,566,566,566,566,566,566,566,566, +566,566,566,566,566,566,566,566,566,566,566,566,566,566,566,566, +566,566,566,566,566,566,566,566,566,566,566,566,566,566,566,566, +566,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +110, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,115,115,115,115, /* block 141 */ -562,562,562,562,562,562,562,562,562,562,562,562,562,562,562,562, -562,562,562,562,562,562,562,562,562,562,562,562,562,562,562,562, -563,563,563,563,114,114,114,114,114,114,114,114,114,114,114,114, -564,564,564,564,564,564,564,564,564,564,564,564,564,564,564,564, -564,565,564,564,564,564,564,564,564,564,565,114,114,114,114,114, -566,566,566,566,566,566,566,566,566,566,566,566,566,566,566,566, -566,566,566,566,566,566,566,566,566,566,566,566,566,566,566,566, -566,566,566,566,566,566,567,567,567,567,567,114,114,114,114,114, +567,567,567,567,567,567,567,567,567,567,567,567,567,567,567,567, +567,567,567,567,567,567,567,567,567,567,567,567,567,567,567,567, +568,568,568,568,115,115,115,115,115,115,115,115,115,115,115,115, +569,569,569,569,569,569,569,569,569,569,569,569,569,569,569,569, +569,570,569,569,569,569,569,569,569,569,570,115,115,115,115,115, +571,571,571,571,571,571,571,571,571,571,571,571,571,571,571,571, +571,571,571,571,571,571,571,571,571,571,571,571,571,571,571,571, +571,571,571,571,571,571,572,572,572,572,572,115,115,115,115,115, /* block 142 */ -568,568,568,568,568,568,568,568,568,568,568,568,568,568,568,568, -568,568,568,568,568,568,568,568,568,568,568,568,568,568,114,569, -570,570,570,570,570,570,570,570,570,570,570,570,570,570,570,570, -570,570,570,570,570,570,570,570,570,570,570,570,570,570,570,570, -570,570,570,570,114,114,114,114,570,570,570,570,570,570,570,570, -571,572,572,572,572,572,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +573,573,573,573,573,573,573,573,573,573,573,573,573,573,573,573, +573,573,573,573,573,573,573,573,573,573,573,573,573,573,115,574, +575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575, +575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575, +575,575,575,575,115,115,115,115,575,575,575,575,575,575,575,575, +576,577,577,577,577,577,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 143 */ -573,573,573,573,573,573,573,573,573,573,573,573,573,573,573,573, -573,573,573,573,573,573,573,573,573,573,573,573,573,573,573,573, -573,573,573,573,573,573,573,573,574,574,574,574,574,574,574,574, -574,574,574,574,574,574,574,574,574,574,574,574,574,574,574,574, -574,574,574,574,574,574,574,574,574,574,574,574,574,574,574,574, -575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575, -575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575, -575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575, +578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578, +578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578, +578,578,578,578,578,578,578,578,579,579,579,579,579,579,579,579, +579,579,579,579,579,579,579,579,579,579,579,579,579,579,579,579, +579,579,579,579,579,579,579,579,579,579,579,579,579,579,579,579, +580,580,580,580,580,580,580,580,580,580,580,580,580,580,580,580, +580,580,580,580,580,580,580,580,580,580,580,580,580,580,580,580, +580,580,580,580,580,580,580,580,580,580,580,580,580,580,580,580, /* block 144 */ -576,576,576,576,576,576,576,576,576,576,576,576,576,576,576,576, -576,576,576,576,576,576,576,576,576,576,576,576,576,576,114,114, -577,577,577,577,577,577,577,577,577,577,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +581,581,581,581,581,581,581,581,581,581,581,581,581,581,581,581, +581,581,581,581,581,581,581,581,581,581,581,581,581,581,115,115, +582,582,582,582,582,582,582,582,582,582,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 145 */ -578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578, -578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578, -578,578,578,578,578,578,578,578,114,114,114,114,114,114,114,114, -579,579,579,579,579,579,579,579,579,579,579,579,579,579,579,579, -579,579,579,579,579,579,579,579,579,579,579,579,579,579,579,579, -579,579,579,579,579,579,579,579,579,579,579,579,579,579,579,579, -579,579,579,579,114,114,114,114,114,114,114,114,114,114,114,580, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +583,583,583,583,583,583,583,583,583,583,583,583,583,583,583,583, +583,583,583,583,583,583,583,583,583,583,583,583,583,583,583,583, +583,583,583,583,583,583,583,583,115,115,115,115,115,115,115,115, +584,584,584,584,584,584,584,584,584,584,584,584,584,584,584,584, +584,584,584,584,584,584,584,584,584,584,584,584,584,584,584,584, +584,584,584,584,584,584,584,584,584,584,584,584,584,584,584,584, +584,584,584,584,115,115,115,115,115,115,115,115,115,115,115,585, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 146 */ -581,581,581,581,581,581,581,581,581,581,581,581,581,581,581,581, -581,581,581,581,581,581,581,581,581,581,581,581,581,581,581,581, -581,581,581,581,581,581,581,581,581,581,581,581,581,581,581,581, -581,581,581,581,581,581,581,581,581,581,581,581,581,581,581,581, -581,581,581,581,581,581,581,581,581,581,581,581,581,581,581,581, -581,581,581,581,581,581,581,581,581,581,581,581,581,581,581,581, -581,581,581,581,581,581,581,581,581,581,581,581,581,581,581,581, -581,581,581,581,581,581,581,581,581,581,581,581,581,581,581,581, +586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586, +586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586, +586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586, +586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586, +586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586, +586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586, +586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586, +586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586, /* block 147 */ -581,581,581,581,581,581,581,581,581,581,581,581,581,581,581,581, -581,581,581,581,581,581,581,581,581,581,581,581,581,581,581,581, -581,581,581,581,581,581,581,581,581,581,581,581,581,581,581,581, -581,581,581,581,581,581,581,114,114,114,114,114,114,114,114,114, -581,581,581,581,581,581,581,581,581,581,581,581,581,581,581,581, -581,581,581,581,581,581,114,114,114,114,114,114,114,114,114,114, -581,581,581,581,581,581,581,581,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586, +586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586, +586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586, +586,586,586,586,586,586,586,115,115,115,115,115,115,115,115,115, +586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586, +586,586,586,586,586,586,115,115,115,115,115,115,115,115,115,115, +586,586,586,586,586,586,586,586,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 148 */ -582,582,582,582,582,582,114,114,582,114,582,582,582,582,582,582, -582,582,582,582,582,582,582,582,582,582,582,582,582,582,582,582, -582,582,582,582,582,582,582,582,582,582,582,582,582,582,582,582, -582,582,582,582,582,582,114,582,582,114,114,114,582,114,114,582, -583,583,583,583,583,583,583,583,583,583,583,583,583,583,583,583, -583,583,583,583,583,583,114,584,585,585,585,585,585,585,585,585, -586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586, -586,586,586,586,586,586,586,587,587,588,588,588,588,588,588,588, +587,587,587,587,587,587,115,115,587,115,587,587,587,587,587,587, +587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587, +587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587, +587,587,587,587,587,587,115,587,587,115,115,115,587,115,115,587, +588,588,588,588,588,588,588,588,588,588,588,588,588,588,588,588, +588,588,588,588,588,588,115,589,590,590,590,590,590,590,590,590, +591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, +591,591,591,591,591,591,591,592,592,593,593,593,593,593,593,593, /* block 149 */ -589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,589, -589,589,589,589,589,589,589,589,589,589,589,589,589,589,589,114, -114,114,114,114,114,114,114,590,590,590,590,590,590,590,590,590, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +594,594,594,594,594,594,594,594,594,594,594,594,594,594,594,594, +594,594,594,594,594,594,594,594,594,594,594,594,594,594,594,115, +115,115,115,115,115,115,115,595,595,595,595,595,595,595,595,595, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +596,596,596,596,596,596,596,596,596,596,596,596,596,596,596,596, +596,596,596,115,596,596,115,115,115,115,115,597,597,597,597,597, /* block 150 */ -591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, -591,591,591,591,591,591,592,592,592,592,592,592,114,114,114,593, -594,594,594,594,594,594,594,594,594,594,594,594,594,594,594,594, -594,594,594,594,594,594,594,594,594,594,114,114,114,114,114,595, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +598,598,598,598,598,598,598,598,598,598,598,598,598,598,598,598, +598,598,598,598,598,598,599,599,599,599,599,599,115,115,115,600, +601,601,601,601,601,601,601,601,601,601,601,601,601,601,601,601, +601,601,601,601,601,601,601,601,601,601,115,115,115,115,115,602, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 151 */ -596,596,596,596,596,596,596,596,596,596,596,596,596,596,596,596, -596,596,596,596,596,596,596,596,596,596,596,596,596,596,596,596, -597,597,597,597,597,597,597,597,597,597,597,597,597,597,597,597, -597,597,597,597,597,597,597,597,114,114,114,114,114,114,597,597, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +603,603,603,603,603,603,603,603,603,603,603,603,603,603,603,603, +603,603,603,603,603,603,603,603,603,603,603,603,603,603,603,603, +604,604,604,604,604,604,604,604,604,604,604,604,604,604,604,604, +604,604,604,604,604,604,604,604,115,115,115,115,605,605,604,604, +605,605,605,605,605,605,605,605,605,605,605,605,605,605,605,605, +115,115,605,605,605,605,605,605,605,605,605,605,605,605,605,605, +605,605,605,605,605,605,605,605,605,605,605,605,605,605,605,605, +605,605,605,605,605,605,605,605,605,605,605,605,605,605,605,605, /* block 152 */ -598,599,599,599,114,599,599,114,114,114,114,114,599,599,599,599, -598,598,598,598,114,598,598,598,114,598,598,598,598,598,598,598, -598,598,598,598,598,598,598,598,598,598,598,598,598,598,598,598, -598,598,598,598,114,114,114,114,599,599,599,114,114,114,114,599, -600,600,600,600,600,600,600,600,114,114,114,114,114,114,114,114, -601,601,601,601,601,601,601,601,601,114,114,114,114,114,114,114, -602,602,602,602,602,602,602,602,602,602,602,602,602,602,602,602, -602,602,602,602,602,602,602,602,602,602,602,602,602,603,603,604, +606,607,607,607,115,607,607,115,115,115,115,115,607,607,607,607, +606,606,606,606,115,606,606,606,115,606,606,606,606,606,606,606, +606,606,606,606,606,606,606,606,606,606,606,606,606,606,606,606, +606,606,606,606,115,115,115,115,607,607,607,115,115,115,115,607, +608,608,608,608,608,608,608,608,115,115,115,115,115,115,115,115, +609,609,609,609,609,609,609,609,609,115,115,115,115,115,115,115, +610,610,610,610,610,610,610,610,610,610,610,610,610,610,610,610, +610,610,610,610,610,610,610,610,610,610,610,610,610,611,611,612, /* block 153 */ -605,605,605,605,605,605,605,605,605,605,605,605,605,605,605,605, -605,605,605,605,605,605,605,605,605,605,605,605,605,606,606,606, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -607,607,607,607,607,607,607,607,608,607,607,607,607,607,607,607, -607,607,607,607,607,607,607,607,607,607,607,607,607,607,607,607, -607,607,607,607,607,609,609,114,114,114,114,610,610,610,610,610, -611,611,611,611,611,611,611,114,114,114,114,114,114,114,114,114, +613,613,613,613,613,613,613,613,613,613,613,613,613,613,613,613, +613,613,613,613,613,613,613,613,613,613,613,613,613,614,614,614, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +615,615,615,615,615,615,615,615,616,615,615,615,615,615,615,615, +615,615,615,615,615,615,615,615,615,615,615,615,615,615,615,615, +615,615,615,615,615,617,617,115,115,115,115,618,618,618,618,618, +619,619,619,619,619,619,619,115,115,115,115,115,115,115,115,115, /* block 154 */ -612,612,612,612,612,612,612,612,612,612,612,612,612,612,612,612, -612,612,612,612,612,612,612,612,612,612,612,612,612,612,612,612, -612,612,612,612,612,612,612,612,612,612,612,612,612,612,612,612, -612,612,612,612,612,612,114,114,114,613,613,613,613,613,613,613, -614,614,614,614,614,614,614,614,614,614,614,614,614,614,614,614, -614,614,614,614,614,614,114,114,615,615,615,615,615,615,615,615, -616,616,616,616,616,616,616,616,616,616,616,616,616,616,616,616, -616,616,616,114,114,114,114,114,617,617,617,617,617,617,617,617, +620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,620, +620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,620, +620,620,620,620,620,620,620,620,620,620,620,620,620,620,620,620, +620,620,620,620,620,620,115,115,115,621,621,621,621,621,621,621, +622,622,622,622,622,622,622,622,622,622,622,622,622,622,622,622, +622,622,622,622,622,622,115,115,623,623,623,623,623,623,623,623, +624,624,624,624,624,624,624,624,624,624,624,624,624,624,624,624, +624,624,624,115,115,115,115,115,625,625,625,625,625,625,625,625, /* block 155 */ -618,618,618,618,618,618,618,618,618,618,618,618,618,618,618,618, -618,618,114,114,114,114,114,114,114,619,619,619,619,114,114,114, -114,114,114,114,114,114,114,114,114,620,620,620,620,620,620,620, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +626,626,626,626,626,626,626,626,626,626,626,626,626,626,626,626, +626,626,115,115,115,115,115,115,115,627,627,627,627,115,115,115, +115,115,115,115,115,115,115,115,115,628,628,628,628,628,628,628, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 156 */ -621,621,621,621,621,621,621,621,621,621,621,621,621,621,621,621, -621,621,621,621,621,621,621,621,621,621,621,621,621,621,621,621, -621,621,621,621,621,621,621,621,621,621,621,621,621,621,621,621, -621,621,621,621,621,621,621,621,621,621,621,621,621,621,621,621, -621,621,621,621,621,621,621,621,621,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +629,629,629,629,629,629,629,629,629,629,629,629,629,629,629,629, +629,629,629,629,629,629,629,629,629,629,629,629,629,629,629,629, +629,629,629,629,629,629,629,629,629,629,629,629,629,629,629,629, +629,629,629,629,629,629,629,629,629,629,629,629,629,629,629,629, +629,629,629,629,629,629,629,629,629,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 157 */ -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -622,622,622,622,622,622,622,622,622,622,622,622,622,622,622,622, -622,622,622,622,622,622,622,622,622,622,622,622,622,622,622,114, +630,630,630,630,630,630,630,630,630,630,630,630,630,630,630,630, +630,630,630,630,630,630,630,630,630,630,630,630,630,630,630,630, +630,630,630,630,630,630,630,630,630,630,630,630,630,630,630,630, +630,630,630,115,115,115,115,115,115,115,115,115,115,115,115,115, +631,631,631,631,631,631,631,631,631,631,631,631,631,631,631,631, +631,631,631,631,631,631,631,631,631,631,631,631,631,631,631,631, +631,631,631,631,631,631,631,631,631,631,631,631,631,631,631,631, +631,631,631,115,115,115,115,115,115,115,632,632,632,632,632,632, /* block 158 */ -623,624,623,625,625,625,625,625,625,625,625,625,625,625,625,625, -625,625,625,625,625,625,625,625,625,625,625,625,625,625,625,625, -625,625,625,625,625,625,625,625,625,625,625,625,625,625,625,625, -625,625,625,625,625,625,625,625,624,624,624,624,624,624,624,624, -624,624,624,624,624,624,624,626,626,626,626,626,626,626,114,114, -114,114,627,627,627,627,627,627,627,627,627,627,627,627,627,627, -627,627,627,627,627,627,628,628,628,628,628,628,628,628,628,628, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,624, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +633,633,633,633,633,633,633,633,633,633,633,633,633,633,633,633, +633,633,633,633,633,633,633,633,633,633,633,633,633,633,633,115, /* block 159 */ -629,629,630,631,631,631,631,631,631,631,631,631,631,631,631,631, -631,631,631,631,631,631,631,631,631,631,631,631,631,631,631,631, -631,631,631,631,631,631,631,631,631,631,631,631,631,631,631,631, -630,630,630,629,629,629,629,630,630,629,629,632,632,633,632,632, -632,632,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -634,634,634,634,634,634,634,634,634,634,634,634,634,634,634,634, -634,634,634,634,634,634,634,634,634,114,114,114,114,114,114,114, -635,635,635,635,635,635,635,635,635,635,114,114,114,114,114,114, +634,635,634,636,636,636,636,636,636,636,636,636,636,636,636,636, +636,636,636,636,636,636,636,636,636,636,636,636,636,636,636,636, +636,636,636,636,636,636,636,636,636,636,636,636,636,636,636,636, +636,636,636,636,636,636,636,636,635,635,635,635,635,635,635,635, +635,635,635,635,635,635,635,637,637,637,637,637,637,637,115,115, +115,115,638,638,638,638,638,638,638,638,638,638,638,638,638,638, +638,638,638,638,638,638,639,639,639,639,639,639,639,639,639,639, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,635, /* block 160 */ -636,636,636,637,637,637,637,637,637,637,637,637,637,637,637,637, -637,637,637,637,637,637,637,637,637,637,637,637,637,637,637,637, -637,637,637,637,637,637,637,636,636,636,636,636,638,636,636,636, -636,636,636,636,636,114,639,639,639,639,639,639,639,639,639,639, -640,640,640,640,114,114,114,114,114,114,114,114,114,114,114,114, -641,641,641,641,641,641,641,641,641,641,641,641,641,641,641,641, -641,641,641,641,641,641,641,641,641,641,641,641,641,641,641,641, -641,641,641,642,643,643,641,114,114,114,114,114,114,114,114,114, +640,640,641,642,642,642,642,642,642,642,642,642,642,642,642,642, +642,642,642,642,642,642,642,642,642,642,642,642,642,642,642,642, +642,642,642,642,642,642,642,642,642,642,642,642,642,642,642,642, +641,641,641,640,640,640,640,641,641,640,640,643,643,644,643,643, +643,643,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +645,645,645,645,645,645,645,645,645,645,645,645,645,645,645,645, +645,645,645,645,645,645,645,645,645,115,115,115,115,115,115,115, +646,646,646,646,646,646,646,646,646,646,115,115,115,115,115,115, /* block 161 */ -644,644,645,646,646,646,646,646,646,646,646,646,646,646,646,646, -646,646,646,646,646,646,646,646,646,646,646,646,646,646,646,646, -646,646,646,646,646,646,646,646,646,646,646,646,646,646,646,646, -646,646,646,645,645,645,644,644,644,644,644,644,644,644,644,645, -645,646,646,646,646,647,647,647,647,114,114,114,114,647,114,114, -648,648,648,648,648,648,648,648,648,648,646,114,114,114,114,114, -114,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649, -649,649,649,649,649,114,114,114,114,114,114,114,114,114,114,114, +647,647,647,648,648,648,648,648,648,648,648,648,648,648,648,648, +648,648,648,648,648,648,648,648,648,648,648,648,648,648,648,648, +648,648,648,648,648,648,648,647,647,647,647,647,649,647,647,647, +647,647,647,647,647,115,650,650,650,650,650,650,650,650,650,650, +651,651,651,651,115,115,115,115,115,115,115,115,115,115,115,115, +652,652,652,652,652,652,652,652,652,652,652,652,652,652,652,652, +652,652,652,652,652,652,652,652,652,652,652,652,652,652,652,652, +652,652,652,653,654,654,652,115,115,115,115,115,115,115,115,115, /* block 162 */ -650,650,650,650,650,650,650,650,650,650,650,650,650,650,650,650, -650,650,114,650,650,650,650,650,650,650,650,650,650,650,650,650, -650,650,650,650,650,650,650,650,650,650,650,650,651,651,651,652, -652,652,651,651,652,651,652,652,653,653,653,653,653,653,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +655,655,656,657,657,657,657,657,657,657,657,657,657,657,657,657, +657,657,657,657,657,657,657,657,657,657,657,657,657,657,657,657, +657,657,657,657,657,657,657,657,657,657,657,657,657,657,657,657, +657,657,657,656,656,656,655,655,655,655,655,655,655,655,655,656, +656,657,657,657,657,658,658,658,658,658,655,655,655,658,115,115, +659,659,659,659,659,659,659,659,659,659,657,658,657,658,658,658, +115,660,660,660,660,660,660,660,660,660,660,660,660,660,660,660, +660,660,660,660,660,115,115,115,115,115,115,115,115,115,115,115, /* block 163 */ -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -654,654,654,654,654,654,654,654,654,654,654,654,654,654,654,654, -654,654,654,654,654,654,654,654,654,654,654,654,654,654,654,654, -654,654,654,654,654,654,654,654,654,654,654,654,654,654,654,655, -656,656,656,655,655,655,655,655,655,655,655,114,114,114,114,114, -657,657,657,657,657,657,657,657,657,657,114,114,114,114,114,114, +661,661,661,661,661,661,661,661,661,661,661,661,661,661,661,661, +661,661,115,661,661,661,661,661,661,661,661,661,661,661,661,661, +661,661,661,661,661,661,661,661,661,661,661,661,662,662,662,663, +663,663,662,662,663,662,663,663,664,664,664,664,664,664,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 164 */ -114,658,659,659,114,660,660,660,660,660,660,660,660,114,114,660, -660,114,114,660,660,660,660,660,660,660,660,660,660,660,660,660, -660,660,660,660,660,660,660,660,660,114,660,660,660,660,660,660, -660,114,660,660,114,660,660,660,660,660,114,114,658,660,661,659, -658,659,659,659,659,114,114,659,659,114,114,659,659,659,114,114, -114,114,114,114,114,114,114,661,114,114,114,114,114,660,660,660, -660,660,659,659,114,114,658,658,658,658,658,658,658,114,114,114, -658,658,658,658,658,114,114,114,114,114,114,114,114,114,114,114, +665,665,665,665,665,665,665,115,665,115,665,665,665,665,115,665, +665,665,665,665,665,665,665,665,665,665,665,665,665,665,115,665, +665,665,665,665,665,665,665,665,665,666,115,115,115,115,115,115, +667,667,667,667,667,667,667,667,667,667,667,667,667,667,667,667, +667,667,667,667,667,667,667,667,667,667,667,667,667,667,667,667, +667,667,667,667,667,667,667,667,667,667,667,667,667,667,667,668, +669,669,669,668,668,668,668,668,668,668,668,115,115,115,115,115, +670,670,670,670,670,670,670,670,670,670,115,115,115,115,115,115, /* block 165 */ -662,662,662,662,662,662,662,662,662,662,662,662,662,662,662,662, -662,662,662,662,662,662,662,662,662,662,662,662,662,662,662,662, -662,662,662,662,662,662,662,662,662,662,662,662,662,662,662,662, -663,664,664,665,665,665,665,665,665,664,665,664,664,663,664,665, -665,664,665,665,662,662,666,662,114,114,114,114,114,114,114,114, -667,667,667,667,667,667,667,667,667,667,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +671,671,672,672,115,673,673,673,673,673,673,673,673,115,115,673, +673,115,115,673,673,673,673,673,673,673,673,673,673,673,673,673, +673,673,673,673,673,673,673,673,673,115,673,673,673,673,673,673, +673,115,673,673,115,673,673,673,673,673,115,115,671,673,674,672, +671,672,672,672,672,115,115,672,672,115,115,672,672,672,115,115, +673,115,115,115,115,115,115,674,115,115,115,115,115,673,673,673, +673,673,672,672,115,115,671,671,671,671,671,671,671,115,115,115, +671,671,671,671,671,115,115,115,115,115,115,115,115,115,115,115, /* block 166 */ -668,668,668,668,668,668,668,668,668,668,668,668,668,668,668,668, -668,668,668,668,668,668,668,668,668,668,668,668,668,668,668,668, -668,668,668,668,668,668,668,668,668,668,668,668,668,668,668,669, -670,670,671,671,671,671,114,114,670,670,670,670,671,671,670,671, -671,672,672,672,672,672,672,672,672,672,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +675,675,675,675,675,675,675,675,675,675,675,675,675,675,675,675, +675,675,675,675,675,675,675,675,675,675,675,675,675,675,675,675, +675,675,675,675,675,675,675,675,675,675,675,675,675,675,675,675, +676,677,677,678,678,678,678,678,678,677,678,677,677,676,677,678, +678,677,678,678,675,675,679,675,115,115,115,115,115,115,115,115, +680,680,680,680,680,680,680,680,680,680,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 167 */ -673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673, -673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673, -673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673, -674,674,674,675,675,675,675,675,675,675,675,674,674,675,674,675, -675,676,676,676,673,114,114,114,114,114,114,114,114,114,114,114, -677,677,677,677,677,677,677,677,677,677,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +681,681,681,681,681,681,681,681,681,681,681,681,681,681,681,681, +681,681,681,681,681,681,681,681,681,681,681,681,681,681,681,681, +681,681,681,681,681,681,681,681,681,681,681,681,681,681,681,682, +683,683,684,684,684,684,115,115,683,683,683,683,684,684,683,684, +684,685,685,685,685,685,685,685,685,685,685,685,685,685,685,685, +685,685,685,685,685,685,685,685,681,681,681,681,684,684,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 168 */ -678,678,678,678,678,678,678,678,678,678,678,678,678,678,678,678, -678,678,678,678,678,678,678,678,678,678,678,678,678,678,678,678, -678,678,678,678,678,678,678,678,678,678,678,679,680,679,680,680, -679,679,679,679,679,679,680,679,114,114,114,114,114,114,114,114, -681,681,681,681,681,681,681,681,681,681,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +686,686,686,686,686,686,686,686,686,686,686,686,686,686,686,686, +686,686,686,686,686,686,686,686,686,686,686,686,686,686,686,686, +686,686,686,686,686,686,686,686,686,686,686,686,686,686,686,686, +687,687,687,688,688,688,688,688,688,688,688,687,687,688,687,688, +688,689,689,689,686,115,115,115,115,115,115,115,115,115,115,115, +690,690,690,690,690,690,690,690,690,690,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 169 */ -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -682,682,682,682,682,682,682,682,682,682,682,682,682,682,682,682, -682,682,682,682,682,682,682,682,682,682,682,682,682,682,682,682, -683,683,683,683,683,683,683,683,683,683,683,683,683,683,683,683, -683,683,683,683,683,683,683,683,683,683,683,683,683,683,683,683, -684,684,684,684,684,684,684,684,684,684,685,685,685,685,685,685, -685,685,685,114,114,114,114,114,114,114,114,114,114,114,114,686, +691,691,691,691,691,691,691,691,691,691,691,691,691,691,691,691, +691,691,691,691,691,691,691,691,691,691,691,691,691,691,691,691, +691,691,691,691,691,691,691,691,691,691,691,692,693,692,693,693, +692,692,692,692,692,692,693,692,115,115,115,115,115,115,115,115, +694,694,694,694,694,694,694,694,694,694,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 170 */ -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -687,687,687,687,687,687,687,687,687,687,687,687,687,687,687,687, -687,687,687,687,687,687,687,687,687,687,687,687,687,687,687,687, -687,687,687,687,687,687,687,687,687,687,687,687,687,687,687,687, -687,687,687,687,687,687,687,687,687,114,114,114,114,114,114,114, +695,695,695,695,695,695,695,695,695,695,695,695,695,695,695,695, +695,695,695,695,695,695,695,695,695,695,115,115,115,696,696,696, +697,697,696,696,696,696,697,696,696,696,696,696,115,115,115,115, +698,698,698,698,698,698,698,698,698,698,699,699,700,700,700,701, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 171 */ -688,688,688,688,688,688,688,688,688,688,688,688,688,688,688,688, -688,688,688,688,688,688,688,688,688,688,688,688,688,688,688,688, -688,688,688,688,688,688,688,688,688,688,688,688,688,688,688,688, -688,688,688,688,688,688,688,688,688,688,688,688,688,688,688,688, -688,688,688,688,688,688,688,688,688,688,688,688,688,688,688,688, -688,688,688,688,688,688,688,688,688,688,688,688,688,688,688,688, -688,688,688,688,688,688,688,688,688,688,688,688,688,688,688,688, -688,688,688,688,688,688,688,688,688,688,688,688,688,688,688,688, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +702,702,702,702,702,702,702,702,702,702,702,702,702,702,702,702, +702,702,702,702,702,702,702,702,702,702,702,702,702,702,702,702, +703,703,703,703,703,703,703,703,703,703,703,703,703,703,703,703, +703,703,703,703,703,703,703,703,703,703,703,703,703,703,703,703, +704,704,704,704,704,704,704,704,704,704,705,705,705,705,705,705, +705,705,705,115,115,115,115,115,115,115,115,115,115,115,115,706, /* block 172 */ -688,688,688,688,688,688,688,688,688,688,688,688,688,688,688,688, -688,688,688,688,688,688,688,688,688,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,707, +707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,707, +707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,707, +707,707,707,707,707,707,707,707,707,115,115,115,115,115,115,115, /* block 173 */ -689,689,689,689,689,689,689,689,689,689,689,689,689,689,689,689, -689,689,689,689,689,689,689,689,689,689,689,689,689,689,689,689, -689,689,689,689,689,689,689,689,689,689,689,689,689,689,689,689, -689,689,689,689,689,689,689,689,689,689,689,689,689,689,689,689, -689,689,689,689,689,689,689,689,689,689,689,689,689,689,689,689, -689,689,689,689,689,689,689,689,689,689,689,689,689,689,689,689, -689,689,689,689,689,689,689,689,689,689,689,689,689,689,689,114, -690,690,690,690,690,114,114,114,114,114,114,114,114,114,114,114, +708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708, +708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708, +708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708, +708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708, +708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708, +708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708, +708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708, +708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708, /* block 174 */ -691,691,691,691,691,691,691,691,691,691,691,691,691,691,691,691, -691,691,691,691,691,691,691,691,691,691,691,691,691,691,691,691, -691,691,691,691,691,691,691,691,691,691,691,691,691,691,691,691, -691,691,691,691,691,691,691,691,691,691,691,691,691,691,691,691, -691,691,691,691,691,691,691,691,691,691,691,691,691,691,691,691, -691,691,691,691,691,691,691,691,691,691,691,691,691,691,691,691, -691,691,691,691,691,691,691,691,691,691,691,691,691,691,691,691, -691,691,691,691,691,691,691,691,691,691,691,691,691,691,691,691, +708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708, +708,708,708,708,708,708,708,708,708,708,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 175 */ -691,691,691,691,691,691,691,691,691,691,691,691,691,691,691,691, -691,691,691,691,691,691,691,691,691,691,691,691,691,691,691,691, -691,691,691,691,691,691,691,691,691,691,691,691,691,691,691,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +709,709,709,709,709,709,709,709,709,709,709,709,709,709,709,709, +709,709,709,709,709,709,709,709,709,709,709,709,709,709,709,709, +709,709,709,709,709,709,709,709,709,709,709,709,709,709,709,709, +709,709,709,709,709,709,709,709,709,709,709,709,709,709,709,709, +709,709,709,709,709,709,709,709,709,709,709,709,709,709,709,709, +709,709,709,709,709,709,709,709,709,709,709,709,709,709,709,709, +709,709,709,709,709,709,709,709,709,709,709,709,709,709,709,115, +710,710,710,710,710,115,115,115,115,115,115,115,115,115,115,115, /* block 176 */ -497,497,497,497,497,497,497,497,497,497,497,497,497,497,497,497, -497,497,497,497,497,497,497,497,497,497,497,497,497,497,497,497, -497,497,497,497,497,497,497,497,497,497,497,497,497,497,497,497, -497,497,497,497,497,497,497,497,497,497,497,497,497,497,497,497, -497,497,497,497,497,497,497,497,497,497,497,497,497,497,497,497, -497,497,497,497,497,497,497,497,497,497,497,497,497,497,497,497, -497,497,497,497,497,497,497,497,497,497,497,497,497,497,497,497, -497,497,497,497,497,497,497,497,497,497,497,497,497,497,497,497, +708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708, +708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708, +708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708, +708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708, +708,708,708,708,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 177 */ -497,497,497,497,497,497,497,497,497,497,497,497,497,497,497,497, -497,497,497,497,497,497,497,497,497,497,497,497,497,497,497,497, -497,497,497,497,497,497,497,497,497,497,497,497,497,497,497,497, -497,497,497,497,497,497,497,497,497,114,114,114,114,114,114,114, -692,692,692,692,692,692,692,692,692,692,692,692,692,692,692,692, -692,692,692,692,692,692,692,692,692,692,692,692,692,692,692,114, -693,693,693,693,693,693,693,693,693,693,114,114,114,114,694,694, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,711, +711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,711, +711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,711, +711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,711, +711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,711, +711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,711, +711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,711, +711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,711, /* block 178 */ -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -695,695,695,695,695,695,695,695,695,695,695,695,695,695,695,695, -695,695,695,695,695,695,695,695,695,695,695,695,695,695,114,114, -696,696,696,696,696,697,114,114,114,114,114,114,114,114,114,114, +711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,711, +711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,711, +711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 179 */ -698,698,698,698,698,698,698,698,698,698,698,698,698,698,698,698, -698,698,698,698,698,698,698,698,698,698,698,698,698,698,698,698, -698,698,698,698,698,698,698,698,698,698,698,698,698,698,698,698, -699,699,699,699,699,699,699,700,700,700,700,700,701,701,701,701, -702,702,702,702,700,701,114,114,114,114,114,114,114,114,114,114, -703,703,703,703,703,703,703,703,703,703,114,704,704,704,704,704, -704,704,114,698,698,698,698,698,698,698,698,698,698,698,698,698, -698,698,698,698,698,698,698,698,114,114,114,114,114,698,698,698, +712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,712, +712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,712, +712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,712, +712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,712, +712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,712, +712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,712, +712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,712, +712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,712, /* block 180 */ -698,698,698,698,698,698,698,698,698,698,698,698,698,698,698,698, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,712, +712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,712, +712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,712, +712,712,712,712,712,712,712,712,712,712,712,712,712,712,712,712, +712,712,712,712,712,712,712,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 181 */ -705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705, -705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705, -705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705, -705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705, -705,705,705,705,705,114,114,114,114,114,114,114,114,114,114,114, -705,706,706,706,706,706,706,706,706,706,706,706,706,706,706,706, -706,706,706,706,706,706,706,706,706,706,706,706,706,706,706,706, -706,706,706,706,706,706,706,706,706,706,706,706,706,706,706,114, +498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498, +498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498, +498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498, +498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498, +498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498, +498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498, +498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498, +498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498, /* block 182 */ -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,707, -707,707,707,708,708,708,708,708,708,708,708,708,708,708,708,708, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498, +498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498, +498,498,498,498,498,498,498,498,498,498,498,498,498,498,498,498, +498,498,498,498,498,498,498,498,498,115,115,115,115,115,115,115, +713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713, +713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,115, +714,714,714,714,714,714,714,714,714,714,115,115,115,115,715,715, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 183 */ -478,476,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +716,716,716,716,716,716,716,716,716,716,716,716,716,716,716,716, +716,716,716,716,716,716,716,716,716,716,716,716,716,716,115,115, +717,717,717,717,717,718,115,115,115,115,115,115,115,115,115,115, /* block 184 */ -709,709,709,709,709,709,709,709,709,709,709,709,709,709,709,709, -709,709,709,709,709,709,709,709,709,709,709,709,709,709,709,709, -709,709,709,709,709,709,709,709,709,709,709,709,709,709,709,709, -709,709,709,709,709,709,709,709,709,709,709,709,709,709,709,709, -709,709,709,709,709,709,709,709,709,709,709,709,709,709,709,709, -709,709,709,709,709,709,709,709,709,709,709,709,709,709,709,709, -709,709,709,709,709,709,709,709,709,709,709,114,114,114,114,114, -709,709,709,709,709,709,709,709,709,709,709,709,709,114,114,114, +719,719,719,719,719,719,719,719,719,719,719,719,719,719,719,719, +719,719,719,719,719,719,719,719,719,719,719,719,719,719,719,719, +719,719,719,719,719,719,719,719,719,719,719,719,719,719,719,719, +720,720,720,720,720,720,720,721,721,721,721,721,722,722,722,722, +723,723,723,723,721,722,115,115,115,115,115,115,115,115,115,115, +724,724,724,724,724,724,724,724,724,724,115,725,725,725,725,725, +725,725,115,719,719,719,719,719,719,719,719,719,719,719,719,719, +719,719,719,719,719,719,719,719,115,115,115,115,115,719,719,719, /* block 185 */ -709,709,709,709,709,709,709,709,709,114,114,114,114,114,114,114, -709,709,709,709,709,709,709,709,709,709,114,114,710,711,711,712, - 22, 22, 22, 22,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +719,719,719,719,719,719,719,719,719,719,719,719,719,719,719,719, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 186 */ - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19,114,114,114,114,114,114,114,114,114,114, +726,726,726,726,726,726,726,726,726,726,726,726,726,726,726,726, +726,726,726,726,726,726,726,726,726,726,726,726,726,726,726,726, +726,726,726,726,726,726,726,726,726,726,726,726,726,726,726,726, +726,726,726,726,726,726,726,726,726,726,726,726,726,726,726,726, +726,726,726,726,726,115,115,115,115,115,115,115,115,115,115,115, +726,727,727,727,727,727,727,727,727,727,727,727,727,727,727,727, +727,727,727,727,727,727,727,727,727,727,727,727,727,727,727,727, +727,727,727,727,727,727,727,727,727,727,727,727,727,727,727,115, /* block 187 */ - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19,114,114, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19,713,405,109,109,109, 19, 19, 19,405,713,713, -713,713,713, 22, 22, 22, 22, 22, 22, 22, 22,109,109,109,109,109, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,728, +728,728,728,729,729,729,729,729,729,729,729,729,729,729,729,729, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 188 */ -109,109,109, 19, 19,109,109,109,109,109,109,109, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,109,109,109,109, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +479,477,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 189 */ -559,559,559,559,559,559,559,559,559,559,559,559,559,559,559,559, -559,559,559,559,559,559,559,559,559,559,559,559,559,559,559,559, -559,559,559,559,559,559,559,559,559,559,559,559,559,559,559,559, -559,559,559,559,559,559,559,559,559,559,559,559,559,559,559,559, -559,559,714,714,714,559,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +730,730,730,730,730,730,730,730,730,730,730,730,730,730,730,730, +730,730,730,730,730,730,730,730,730,730,730,730,730,730,730,730, +730,730,730,730,730,730,730,730,730,730,730,730,730,730,730,730, +730,730,730,730,730,730,730,730,730,730,730,730,730,730,730,730, +730,730,730,730,730,730,730,730,730,730,730,730,730,730,730,730, +730,730,730,730,730,730,730,730,730,730,730,730,730,730,730,730, +730,730,730,730,730,730,730,730,730,730,730,115,115,115,115,115, +730,730,730,730,730,730,730,730,730,730,730,730,730,115,115,115, /* block 190 */ - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19,114,114,114,114,114,114,114,114,114, - 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, - 23, 23,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +730,730,730,730,730,730,730,730,730,115,115,115,115,115,115,115, +730,730,730,730,730,730,730,730,730,730,115,115,731,732,732,733, + 22, 22, 22, 22,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 191 */ -437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437, -437,437,437,437,437,437,437,437,437,437,438,438,438,438,438,438, -438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438, -438,438,438,438,437,437,437,437,437,437,437,437,437,437,437,437, -437,437,437,437,437,437,437,437,437,437,437,437,437,437,438,438, -438,438,438,438,438,114,438,438,438,438,438,438,438,438,438,438, -438,438,438,438,438,438,438,438,437,437,437,437,437,437,437,437, -437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19,115,115,115,115,115,115,115,115,115,115, /* block 192 */ -437,437,438,438,438,438,438,438,438,438,438,438,438,438,438,438, -438,438,438,438,438,438,438,438,438,438,438,438,437,114,437,437, -114,114,437,114,114,437,437,114,114,437,437,437,437,114,437,437, -437,437,437,437,437,437,438,438,438,438,114,438,114,438,438,438, -438,438,438,438,114,438,438,438,438,438,438,438,438,438,438,438, -437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437, -437,437,437,437,437,437,437,437,437,437,438,438,438,438,438,438, -438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19,115,115, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19,734,406,110,110,110, 19, 19, 19,406,734,734, +734,734,734, 22, 22, 22, 22, 22, 22, 22, 22,110,110,110,110,110, /* block 193 */ -438,438,438,438,437,437,114,437,437,437,437,114,114,437,437,437, -437,437,437,437,437,114,437,437,437,437,437,437,437,114,438,438, -438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438, -438,438,438,438,438,438,438,438,437,437,114,437,437,437,437,114, -437,437,437,437,437,114,437,114,114,114,437,437,437,437,437,437, -437,114,438,438,438,438,438,438,438,438,438,438,438,438,438,438, -438,438,438,438,438,438,438,438,438,438,438,438,437,437,437,437, -437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437, +110,110,110, 19, 19,110,110,110,110,110,110,110, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,110,110,110,110, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 194 */ -437,437,437,437,437,437,438,438,438,438,438,438,438,438,438,438, -438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438, -437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437, -437,437,437,437,437,437,437,437,437,437,438,438,438,438,438,438, -438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438, -438,438,438,438,437,437,437,437,437,437,437,437,437,437,437,437, -437,437,437,437,437,437,437,437,437,437,437,437,437,437,438,438, -438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438, +564,564,564,564,564,564,564,564,564,564,564,564,564,564,564,564, +564,564,564,564,564,564,564,564,564,564,564,564,564,564,564,564, +564,564,564,564,564,564,564,564,564,564,564,564,564,564,564,564, +564,564,564,564,564,564,564,564,564,564,564,564,564,564,564,564, +564,564,735,735,735,564,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 195 */ -438,438,438,438,438,438,438,438,437,437,437,437,437,437,437,437, -437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437, -437,437,438,438,438,438,438,438,438,438,438,438,438,438,438,438, -438,438,438,438,438,438,438,438,438,438,438,438,437,437,437,437, -437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437, -437,437,437,437,437,437,438,438,438,438,438,438,438,438,438,438, -438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438, -437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19,115,115,115,115,115,115,115,115,115, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 196 */ -437,437,437,437,437,437,437,437,437,437,438,438,438,438,438,438, 438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438, -438,438,438,438,438,438,114,114,437,437,437,437,437,437,437,437, -437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437, -437, 8,438,438,438,438,438,438,438,438,438,438,438,438,438,438, -438,438,438,438,438,438,438,438,438,438,438, 8,438,438,438,438, -438,438,437,437,437,437,437,437,437,437,437,437,437,437,437,437, -437,437,437,437,437,437,437,437,437,437,437, 8,438,438,438,438, +438,438,438,438,438,438,438,438,438,438,439,439,439,439,439,439, +439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,439, +439,439,439,439,438,438,438,438,438,438,438,438,438,438,438,438, +438,438,438,438,438,438,438,438,438,438,438,438,438,438,439,439, +439,439,439,439,439,115,439,439,439,439,439,439,439,439,439,439, +439,439,439,439,439,439,439,439,438,438,438,438,438,438,438,438, +438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438, /* block 197 */ +438,438,439,439,439,439,439,439,439,439,439,439,439,439,439,439, +439,439,439,439,439,439,439,439,439,439,439,439,438,115,438,438, +115,115,438,115,115,438,438,115,115,438,438,438,438,115,438,438, +438,438,438,438,438,438,439,439,439,439,115,439,115,439,439,439, +439,439,439,439,115,439,439,439,439,439,439,439,439,439,439,439, 438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438, -438,438,438,438,438, 8,438,438,438,438,438,438,437,437,437,437, -437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437, -437,437,437,437,437, 8,438,438,438,438,438,438,438,438,438,438, -438,438,438,438,438,438,438,438,438,438,438,438,438,438,438, 8, -438,438,438,438,438,438,437,437,437,437,437,437,437,437,437,437, -437,437,437,437,437,437,437,437,437,437,437,437,437,437,437, 8, -438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438, +438,438,438,438,438,438,438,438,438,438,439,439,439,439,439,439, +439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,439, /* block 198 */ -438,438,438,438,438,438,438,438,438, 8,438,438,438,438,438,438, -437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437, -437,437,437,437,437,437,437,437,437, 8,438,438,438,438,438,438, +439,439,439,439,438,438,115,438,438,438,438,115,115,438,438,438, +438,438,438,438,438,115,438,438,438,438,438,438,438,115,439,439, +439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,439, +439,439,439,439,439,439,439,439,438,438,115,438,438,438,438,115, +438,438,438,438,438,115,438,115,115,115,438,438,438,438,438,438, +438,115,439,439,439,439,439,439,439,439,439,439,439,439,439,439, +439,439,439,439,439,439,439,439,439,439,439,439,438,438,438,438, 438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438, -438,438,438, 8,438,438,438,438,438,438,437,438,114,114, 10, 10, - 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, - 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, - 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, /* block 199 */ -715,715,715,715,715,715,715,715,715,715,715,715,715,715,715,715, -715,715,715,715,715,715,715,715,715,715,715,715,715,715,715,715, -715,715,715,715,715,715,715,715,715,715,715,715,715,715,715,715, -715,715,715,715,715,715,715,715,715,715,715,715,715,715,715,715, -715,715,715,715,715,715,715,715,715,715,715,715,715,715,715,715, -715,715,715,715,715,715,715,715,715,715,715,715,715,715,715,715, -715,715,715,715,715,715,715,715,715,715,715,715,715,715,715,715, -715,715,715,715,715,715,715,715,715,715,715,715,715,715,715,715, +438,438,438,438,438,438,439,439,439,439,439,439,439,439,439,439, +439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,439, +438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438, +438,438,438,438,438,438,438,438,438,438,439,439,439,439,439,439, +439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,439, +439,439,439,439,438,438,438,438,438,438,438,438,438,438,438,438, +438,438,438,438,438,438,438,438,438,438,438,438,438,438,439,439, +439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,439, /* block 200 */ -715,715,715,715,715,715,715,715,715,715,715,715,715,715,715,715, -715,715,715,715,715,715,715,715,715,715,715,715,715,715,715,715, -715,715,715,715,715,715,715,715,715,715,715,715,715,715,715,715, -715,715,715,715,715,715,715,715,715,715,715,715,715,715,715,715, -715,715,715,715,715,114,114,716,716,716,716,716,716,716,716,716, -717,717,717,717,717,717,717,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +439,439,439,439,439,439,439,439,438,438,438,438,438,438,438,438, +438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438, +438,438,439,439,439,439,439,439,439,439,439,439,439,439,439,439, +439,439,439,439,439,439,439,439,439,439,439,439,438,438,438,438, +438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438, +438,438,438,438,438,438,439,439,439,439,439,439,439,439,439,439, +439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,439, +438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438, /* block 201 */ -199,199,199,199,114,199,199,199,199,199,199,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,199,199,199,199, -114,199,199,114,199,114,114,199,114,199,199,199,199,199,199,199, -199,199,199,114,199,199,199,199,114,199,114,199,114,114,114,114, -114,114,199,114,114,114,114,199,114,199,114,199,114,199,199,199, -114,199,199,114,199,114,114,199,114,199,114,199,114,199,114,199, -114,199,199,114,199,114,114,199,199,199,199,114,199,199,199,199, -199,199,199,114,199,199,199,199,114,199,199,199,199,114,199,114, +438,438,438,438,438,438,438,438,438,438,439,439,439,439,439,439, +439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,439, +439,439,439,439,439,439,115,115,438,438,438,438,438,438,438,438, +438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438, +438, 8,439,439,439,439,439,439,439,439,439,439,439,439,439,439, +439,439,439,439,439,439,439,439,439,439,439, 8,439,439,439,439, +439,439,438,438,438,438,438,438,438,438,438,438,438,438,438,438, +438,438,438,438,438,438,438,438,438,438,438, 8,439,439,439,439, /* block 202 */ -199,199,199,199,199,199,199,199,199,199,114,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,114,114,114,114, -114,199,199,199,114,199,199,199,199,199,114,199,199,199,199,199, -199,199,199,199,199,199,199,199,199,199,199,199,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -194,194,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,439, +439,439,439,439,439, 8,439,439,439,439,439,439,438,438,438,438, +438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438, +438,438,438,438,438, 8,439,439,439,439,439,439,439,439,439,439, +439,439,439,439,439,439,439,439,439,439,439,439,439,439,439, 8, +439,439,439,439,439,439,438,438,438,438,438,438,438,438,438,438, +438,438,438,438,438,438,438,438,438,438,438,438,438,438,438, 8, +439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,439, /* block 203 */ - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,114,114,114,114, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, +439,439,439,439,439,439,439,439,439, 8,439,439,439,439,439,439, +438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438, +438,438,438,438,438,438,438,438,438, 8,439,439,439,439,439,439, +439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,439, +439,439,439, 8,439,439,439,439,439,439,438,439,115,115, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, /* block 204 */ - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19,114,114,114,114,114,114,114,114,114,114,114,114, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,114, -114, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, -114, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, -114, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19,114,114,114,114,114,114,114,114,114,114, +736,736,736,736,736,736,736,736,736,736,736,736,736,736,736,736, +736,736,736,736,736,736,736,736,736,736,736,736,736,736,736,736, +736,736,736,736,736,736,736,736,736,736,736,736,736,736,736,736, +736,736,736,736,736,736,736,736,736,736,736,736,736,736,736,736, +736,736,736,736,736,736,736,736,736,736,736,736,736,736,736,736, +736,736,736,736,736,736,736,736,736,736,736,736,736,736,736,736, +736,736,736,736,736,736,736,736,736,736,736,736,736,736,736,736, +736,736,736,736,736,736,736,736,736,736,736,736,736,736,736,736, /* block 205 */ - 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,114,114,114, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,114, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,114,114,114,114, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, +737,737,737,737,737,737,737,737,737,737,737,737,737,737,737,737, +737,737,737,737,737,737,737,737,737,737,737,737,737,737,737,737, +737,737,737,737,737,737,737,737,737,737,737,737,737,737,737,737, +737,737,737,737,737,737,737,736,736,736,736,737,737,737,737,737, +737,737,737,737,737,737,737,737,737,737,737,737,737,737,737,737, +737,737,737,737,737,737,737,737,737,737,737,737,737,737,737,737, +737,737,737,737,737,737,737,737,737,737,737,737,737,736,736,736, +736,736,736,736,736,737,736,736,736,736,736,736,736,736,736,736, /* block 206 */ - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,718,718,718,718,718,718,718,718,718,718, -718,718,718,718,718,718,718,718,718,718,718,718,718,718,718,718, +736,736,736,736,737,736,736,738,738,738,738,738,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,737,737,737,737,737, +115,737,737,737,737,737,737,737,737,737,737,737,737,737,737,737, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 207 */ -719, 19, 19,114,114,114,114,114,114,114,114,114,114,114,114,114, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,114,114,114,114,114, - 19, 19, 19, 19, 19, 19, 19, 19, 19,114,114,114,114,114,114,114, - 19, 19,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,739, +739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,739, +739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,739, +739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,739, +739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,739, +739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,739, +739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,739, +739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,739, /* block 208 */ - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,114,114,114, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,114,114, +739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,739, +739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,739, +739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,739, +739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,739, +739,739,739,739,739,115,115,740,740,740,740,740,740,740,740,740, +741,741,741,741,741,741,741,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 209 */ - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,114, -114,114,114,114, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19,114,114,114,114,114,114,114,114, +200,200,200,200,115,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +115,200,200,115,200,115,115,200,115,200,200,200,200,200,200,200, +200,200,200,115,200,200,200,200,115,200,115,200,115,115,115,115, +115,115,200,115,115,115,115,200,115,200,115,200,115,200,200,200, +115,200,200,115,200,115,115,200,115,200,115,200,115,200,115,200, +115,200,200,115,200,115,115,200,200,200,200,115,200,200,200,200, +200,200,200,115,200,200,200,200,115,200,200,200,200,115,200,115, /* block 210 */ - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,114, +200,200,200,200,200,200,200,200,200,200,115,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,115,115,115,115, +115,200,200,200,115,200,200,200,200,200,115,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +195,195,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 211 */ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,114,114,114,114,114, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,114, 19, 19, 19, 19, 19, /* block 212 */ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19,115,115,115,115,115,115,115,115,115,115,115,115, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115, +115, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, +115, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, +115, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19,114, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19,115,115,115,115,115,115,115,115,115,115, /* block 213 */ + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,115,115,115, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19,114,114, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, /* block 214 */ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,114,114,114, - 19, 19, 19, 19,114,114,114,114,114,114,114,114,114,114,114,114, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,742,742,742,742,742,742,742,742,742,742, +742,742,742,742,742,742,742,742,742,742,742,742,742,742,742,742, /* block 215 */ +743, 19, 19,115,115,115,115,115,115,115,115,115,115,115,115,115, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19,114,114,114,114,114,114,114,114,114,114,114,114, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115,115, + 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115,115,115,115, + 19, 19,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 216 */ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, @@ -3519,109 +3543,199 @@ const uint16_t PRIV(ucd_stage2)[] = { /* 58112 bytes, block = 128 */ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 14, 14, 14, 14, 14, /* block 217 */ - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,114,114,114,114, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19,114,114,114,114,114,114,114,114, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,114,114,114,114,114,114, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115, 19, 19, 19, 19, 19, /* block 218 */ - 19, 19, 19, 19, 19, 19, 19, 19,114,114,114,114,114,114,114,114, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19,115, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, /* block 219 */ -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115, + 19, 19, 19, 19,115,115,115,115,115,115,115,115,115,115,115,115, /* block 220 */ -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,114,114,114,114,114,114,114,114,114,114,114, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19,115,115,115,115,115,115,115,115,115,115,115,115, /* block 221 */ -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, -114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 222 */ -436, 22,436,436,436,436,436,436,436,436,436,436,436,436,436,436, -436,436,436,436,436,436,436,436,436,436,436,436,436,436,436,436, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115,115,115,115,115, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115,115,115, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, /* block 223 */ -436,436,436,436,436,436,436,436,436,436,436,436,436,436,436,436, -436,436,436,436,436,436,436,436,436,436,436,436,436,436,436,436, -436,436,436,436,436,436,436,436,436,436,436,436,436,436,436,436, -436,436,436,436,436,436,436,436,436,436,436,436,436,436,436,436, -436,436,436,436,436,436,436,436,436,436,436,436,436,436,436,436, -436,436,436,436,436,436,436,436,436,436,436,436,436,436,436,436, -436,436,436,436,436,436,436,436,436,436,436,436,436,436,436,436, -436,436,436,436,436,436,436,436,436,436,436,436,436,436,436,436, + 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115,115,115,115,115, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 224 */ -109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, -109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, -109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, -109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, -109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, -109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, -109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, -109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, + 19, 19, 19, 19, 19, 19, 19, 19, 19,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 225 */ -109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, -109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, -109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, -109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, -109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, -109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, -109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, -436,436,436,436,436,436,436,436,436,436,436,436,436,436,436,436, + 19, 19, 19, 19, 19,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, + 19,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, /* block 226 */ -552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552, -552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552, -552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552, -552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552, -552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552, -552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552, -552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552, -552,552,552,552,552,552,552,552,552,552,552,552,552,552,114,114, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, + +/* block 227 */ +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,115,115,115,115,115,115,115,115,115,115,115, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, + +/* block 228 */ +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,115,115, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, + +/* block 229 */ +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, + +/* block 230 */ +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, + +/* block 231 */ +437, 22,437,437,437,437,437,437,437,437,437,437,437,437,437,437, +437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + +/* block 232 */ +437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437, +437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437, +437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437, +437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437, +437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437, +437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437, +437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437, +437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437, + +/* block 233 */ +110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, +110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, +110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, +110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, +110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, +110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, +110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, +110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, + +/* block 234 */ +110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, +110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, +110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, +110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, +110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, +110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, +110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, +437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437, + +/* block 235 */ +557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557, +557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557, +557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557, +557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557, +557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557, +557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557, +557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557, +557,557,557,557,557,557,557,557,557,557,557,557,557,557,115,115, }; diff --git a/pcre2-10.20/src/pcre2_ucp.h b/pcre2-10.21/src/pcre2_ucp.h similarity index 96% rename from pcre2-10.20/src/pcre2_ucp.h rename to pcre2-10.21/src/pcre2_ucp.h index e7db0c015..0b7553e5e 100644 --- a/pcre2-10.20/src/pcre2_ucp.h +++ b/pcre2-10.21/src/pcre2_ucp.h @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2014 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -253,7 +253,14 @@ enum { ucp_Pau_Cin_Hau, ucp_Siddham, ucp_Tirhuta, - ucp_Warang_Citi + ucp_Warang_Citi, + /* New for Unicode 8.0.0: */ + ucp_Ahom, + ucp_Anatolian_Hieroglyphs, + ucp_Hatran, + ucp_Multani, + ucp_Old_Hungarian, + ucp_SignWriting }; #endif diff --git a/pcre2-10.20/src/pcre2_valid_utf.c b/pcre2-10.21/src/pcre2_valid_utf.c similarity index 98% rename from pcre2-10.20/src/pcre2_valid_utf.c rename to pcre2-10.21/src/pcre2_valid_utf.c index a97847ab9..2dfd8df34 100644 --- a/pcre2-10.20/src/pcre2_valid_utf.c +++ b/pcre2-10.21/src/pcre2_valid_utf.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2014 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -131,11 +131,13 @@ PCRE2_ERROR_UTF8_ERR20 Isolated 0x80 byte (not within UTF-8 character) PCRE2_ERROR_UTF8_ERR21 Byte with the illegal value 0xfe or 0xff */ -for (p = string; length-- > 0; p++) +for (p = string; length > 0; p++) { register uint32_t ab, d; c = *p; + length--; + if (c < 128) continue; /* ASCII character */ if (c < 0xc0) /* Isolated 10xx xxxx byte */ @@ -324,9 +326,10 @@ PCRE2_ERROR_UTF16_ERR2 Invalid low surrogate PCRE2_ERROR_UTF16_ERR3 Isolated low surrogate */ -for (p = string; length-- > 0; p++) +for (p = string; length > 0; p++) { c = *p; + length--; if ((c & 0xf800) != 0xd800) { @@ -368,7 +371,7 @@ PCRE2_ERROR_UTF32_ERR1 Surrogate character PCRE2_ERROR_UTF32_ERR2 Character > 0x10ffff */ -for (p = string; length-- > 0; p++) +for (p = string; length > 0; length--, p++) { c = *p; if ((c & 0xfffff800u) != 0xd800u) diff --git a/pcre2-10.20/src/pcre2_xclass.c b/pcre2-10.21/src/pcre2_xclass.c similarity index 98% rename from pcre2-10.20/src/pcre2_xclass.c rename to pcre2-10.21/src/pcre2_xclass.c index 2ea89c4b8..407d3f5b8 100644 --- a/pcre2-10.20/src/pcre2_xclass.c +++ b/pcre2-10.21/src/pcre2_xclass.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2014 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -247,7 +247,7 @@ while ((t = *data++) != XCL_END) case PT_PXPUNCT: if ((PRIV(ucp_gentype)[prop->chartype] == ucp_P || - (c < 256 && PRIV(ucp_gentype)[prop->chartype] == ucp_S)) == isprop) + (c < 128 && PRIV(ucp_gentype)[prop->chartype] == ucp_S)) == isprop) return !negated; break; diff --git a/pcre2-10.20/src/pcre2demo.c b/pcre2-10.21/src/pcre2demo.c similarity index 100% rename from pcre2-10.20/src/pcre2demo.c rename to pcre2-10.21/src/pcre2demo.c diff --git a/pcre2-10.20/src/pcre2grep.c b/pcre2-10.21/src/pcre2grep.c similarity index 99% rename from pcre2-10.20/src/pcre2grep.c rename to pcre2-10.21/src/pcre2grep.c index d5a5d6db9..aadb22a57 100644 --- a/pcre2-10.20/src/pcre2grep.c +++ b/pcre2-10.21/src/pcre2grep.c @@ -13,7 +13,7 @@ distribution because other apparatus is needed to compile pcre2grep for z/OS. The header can be found in the special z/OS distribution, which is available from www.zaconsultants.net or from www.cbttape.org. - Copyright (c) 1997-2014 University of Cambridge + Copyright (c) 1997-2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -1691,9 +1691,13 @@ while (ptr < endptr) if (filenames == FN_NOMATCH_ONLY) return 1; + /* If all we want is a yes/no answer, we can return immediately. */ + + if (quiet) return 0; + /* Just count if just counting is wanted. */ - if (count_only) count++; + else if (count_only) count++; /* When handling a binary file and binary-files==binary, the "binary" variable will be set true (it's false in all other cases). In this @@ -1705,8 +1709,8 @@ while (ptr < endptr) return 0; } - /* If all we want is a file name, there is no need to scan any more lines - in the file. */ + /* Likewise, if all we want is a file name, there is no need to scan any + more lines in the file. */ else if (filenames == FN_MATCH_ONLY) { @@ -1714,10 +1718,6 @@ while (ptr < endptr) return 0; } - /* Likewise, if all we want is a yes/no answer. */ - - else if (quiet) return 0; - /* The --only-matching option prints just the substring that matched, and/or one or more captured portions of it, as long as these strings are not empty. The --file-offsets and --line-offsets options output offsets for @@ -2069,7 +2069,7 @@ if (filenames == FN_NOMATCH_ONLY) /* Print the match count if wanted */ -if (count_only) +if (count_only && !quiet) { if (count > 0 || !omit_zero_count) { @@ -2421,7 +2421,7 @@ return options; static char * ordin(int n) { -static char buffer[8]; +static char buffer[14]; char *p = buffer; sprintf(p, "%d", n); while (*p != 0) p++; diff --git a/pcre2-10.20/src/pcre2posix.c b/pcre2-10.21/src/pcre2posix.c similarity index 93% rename from pcre2-10.20/src/pcre2posix.c rename to pcre2-10.21/src/pcre2posix.c index da212fc4d..1d6e5b797 100644 --- a/pcre2-10.20/src/pcre2posix.c +++ b/pcre2-10.21/src/pcre2posix.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2014 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -106,7 +106,7 @@ static const int eint1[] = { static const int eint2[] = { 30, REG_ECTYPE, /* unknown POSIX class name */ - 32, REG_INVARG, /* this version of PCRE does not have UTF or UCP support */ + 32, REG_INVARG, /* this version of PCRE2 does not have Unicode support */ 37, REG_EESCAPE, /* PCRE2 does not support \L, \l, \N{name}, \U, or \u */ 56, REG_INVARG, /* internal error: unknown newline setting */ }; @@ -144,29 +144,23 @@ static const char *const pstring[] = { PCRE2POSIX_EXP_DEFN size_t PCRE2_CALL_CONVENTION regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size) { -const char *message, *addmessage; -size_t length, addlength; +int used; +const char *message; -message = (errcode >= (int)(sizeof(pstring)/sizeof(char *)))? +message = (errcode <= 0 || errcode >= (int)(sizeof(pstring)/sizeof(char *)))? "unknown error code" : pstring[errcode]; -length = strlen(message) + 1; -addmessage = " at offset "; -addlength = (preg != NULL && (int)preg->re_erroffset != -1)? - strlen(addmessage) + 6 : 0; - -if (errbuf_size > 0) +if (preg != NULL && (int)preg->re_erroffset != -1) { - if (addlength > 0 && errbuf_size >= length + addlength) - sprintf(errbuf, "%s%s%-6d", message, addmessage, (int)preg->re_erroffset); - else - { - strncpy(errbuf, message, errbuf_size - 1); - errbuf[errbuf_size-1] = 0; - } + used = snprintf(errbuf, errbuf_size, "%s at offset %-6d", message, + (int)preg->re_erroffset); + } +else + { + used = snprintf(errbuf, errbuf_size, "%s", message); } -return length + addlength; +return used + 1; } @@ -223,8 +217,13 @@ preg->re_erroffset = erroffset; if (preg->re_pcre2_code == NULL) { unsigned int i; - if (errorcode < 0) return REG_BADPAT; /* UTF error */ + + /* A negative value is a UTF error; otherwise all error codes are greater + than COMPILE_ERROR_BASE, but check, just in case. */ + + if (errorcode < COMPILE_ERROR_BASE) return REG_BADPAT; errorcode -= COMPILE_ERROR_BASE; + if (errorcode < (int)(sizeof(eint1)/sizeof(const int))) return eint1[errorcode]; for (i = 0; i < sizeof(eint2)/(2*sizeof(const int)); i += 2) @@ -237,6 +236,13 @@ if (preg->re_pcre2_code == NULL) preg->re_nsub = (size_t)re_nsub; if ((options & PCRE2_NO_AUTO_CAPTURE) != 0) re_nsub = -1; preg->re_match_data = pcre2_match_data_create(re_nsub + 1, NULL); + +if (preg->re_match_data == NULL) + { + pcre2_code_free(preg->re_pcre2_code); + return REG_ESPACE; + } + return 0; } @@ -279,6 +285,7 @@ start location rather than being passed as a PCRE2 "starting offset". */ if ((eflags & REG_STARTEND) != 0) { + if (pmatch == NULL) return REG_INVARG; so = pmatch[0].rm_so; eo = pmatch[0].rm_eo; } diff --git a/pcre2-10.20/src/pcre2posix.h b/pcre2-10.21/src/pcre2posix.h similarity index 98% rename from pcre2-10.20/src/pcre2posix.h rename to pcre2-10.21/src/pcre2posix.h index 6f19b51b2..44a2fd8ad 100644 --- a/pcre2-10.20/src/pcre2posix.h +++ b/pcre2-10.21/src/pcre2posix.h @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2014 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without diff --git a/pcre2-10.20/src/pcre2test.c b/pcre2-10.21/src/pcre2test.c similarity index 86% rename from pcre2-10.20/src/pcre2test.c rename to pcre2-10.21/src/pcre2test.c index 34cc3a5ed..0a5879e47 100644 --- a/pcre2-10.20/src/pcre2test.c +++ b/pcre2-10.21/src/pcre2test.c @@ -11,7 +11,7 @@ hacked-up (non-) design had also run out of steam. Written by Philip Hazel Original code Copyright (c) 1997-2012 University of Cambridge - Rewritten code Copyright (c) 2015 University of Cambridge + Rewritten code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -66,6 +66,14 @@ it references only the enabled library functions. */ #include #include +#if defined NATIVE_ZOS +#include "pcrzoscs.h" +/* That header is not included in the main PCRE2 distribution because other +apparatus is needed to compile pcre2test for z/OS. The header can be found in +the special z/OS distribution, which is available from www.zaconsultants.net or +from www.cbttape.org. */ +#endif + #ifdef HAVE_UNISTD_H #include #endif @@ -174,13 +182,13 @@ void vms_setsymbol( char *, char *, int ); #define LOCALESIZE 32 /* Size of locale name */ #define LOOPREPEAT 500000 /* Default loop count for timing */ #define PATSTACKSIZE 20 /* Pattern stack for save/restore testing */ -#define REPLACE_MODSIZE 96 /* Field for reading 8-bit replacement */ +#define REPLACE_MODSIZE 100 /* Field for reading 8-bit replacement */ #define VERSION_SIZE 64 /* Size of buffer for the version strings */ /* Make sure the buffer into which replacement strings are copied is big enough to hold them as 32-bit code units. */ -#define REPLACE_BUFFSIZE (4*REPLACE_MODSIZE) +#define REPLACE_BUFFSIZE 1024 /* This is a byte value */ /* Execution modes */ @@ -231,6 +239,22 @@ of PRIV avoids name clashes. */ #include "pcre2_tables.c" #include "pcre2_ucd.c" +/* 32-bit integer values in the input are read by strtoul() or strtol(). The +check needed for overflow depends on whether long ints are in fact longer than +ints. They are defined not to be shorter. */ + +#if ULONG_MAX > UINT32_MAX +#define U32OVERFLOW(x) (x > UINT32_MAX) +#else +#define U32OVERFLOW(x) (x == UINT32_MAX) +#endif + +#if LONG_MAX > INT32_MAX +#define S32OVERFLOW(x) (x > INT32_MAX || x < INT32_MIN) +#else +#define S32OVERFLOW(x) (x == INT32_MAX || x == INT32_MIN) +#endif + /* When PCRE2_CODE_UNIT_WIDTH is zero, pcre2_internal.h does not include pcre2_intmodedep.h, which is where mode-dependent macros and structures are defined. We can now include it for each supported code unit width. Because @@ -328,17 +352,18 @@ typedef struct cmdstruct { int value; } cmdstruct; -enum { CMD_FORBID_UTF, CMD_LOAD, CMD_PATTERN, CMD_PERLTEST, CMD_POP, CMD_SAVE, - CMD_SUBJECT, CMD_UNKNOWN }; +enum { CMD_FORBID_UTF, CMD_LOAD, CMD_NEWLINE_DEFAULT, CMD_PATTERN, + CMD_PERLTEST, CMD_POP, CMD_SAVE, CMD_SUBJECT, CMD_UNKNOWN }; static cmdstruct cmdlist[] = { - { "forbid_utf", CMD_FORBID_UTF }, - { "load", CMD_LOAD }, - { "pattern", CMD_PATTERN }, - { "perltest", CMD_PERLTEST }, - { "pop", CMD_POP }, - { "save", CMD_SAVE }, - { "subject", CMD_SUBJECT }}; + { "forbid_utf", CMD_FORBID_UTF }, + { "load", CMD_LOAD }, + { "newline_default", CMD_NEWLINE_DEFAULT }, + { "pattern", CMD_PATTERN }, + { "perltest", CMD_PERLTEST }, + { "pop", CMD_POP }, + { "save", CMD_SAVE }, + { "subject", CMD_SUBJECT }}; #define cmdlistcount sizeof(cmdlist)/sizeof(cmdstruct) @@ -370,38 +395,54 @@ enum { MOD_CTC, /* Applies to a compile context */ MOD_NL, /* Is a newline value */ MOD_NN, /* Is a number or a name; more than one may occur */ MOD_OPT, /* Is an option bit */ + MOD_SIZ, /* Is a PCRE2_SIZE value */ MOD_STR }; /* Is a string */ /* Control bits. Some apply to compiling, some to matching, but some can be set -either on a pattern or a data line, so they must all be distinct. */ +either on a pattern or a data line, so they must all be distinct. There are now +so many of them that they are split into two fields. */ -#define CTL_AFTERTEXT 0x00000001u -#define CTL_ALLAFTERTEXT 0x00000002u -#define CTL_ALLCAPTURES 0x00000004u -#define CTL_ALLUSEDTEXT 0x00000008u -#define CTL_ALTGLOBAL 0x00000010u -#define CTL_BINCODE 0x00000020u -#define CTL_CALLOUT_CAPTURE 0x00000040u -#define CTL_CALLOUT_INFO 0x00000080u -#define CTL_CALLOUT_NONE 0x00000100u -#define CTL_DFA 0x00000200u -#define CTL_FINDLIMITS 0x00000400u -#define CTL_FULLBINCODE 0x00000800u -#define CTL_GETALL 0x00001000u -#define CTL_GLOBAL 0x00002000u -#define CTL_HEXPAT 0x00004000u -#define CTL_INFO 0x00008000u -#define CTL_JITFAST 0x00010000u -#define CTL_JITVERIFY 0x00020000u -#define CTL_MARK 0x00040000u -#define CTL_MEMORY 0x00080000u -#define CTL_POSIX 0x00100000u -#define CTL_PUSH 0x00200000u -#define CTL_STARTCHAR 0x00400000u -#define CTL_ZERO_TERMINATE 0x00800000u +#define CTL_AFTERTEXT 0x00000001u +#define CTL_ALLAFTERTEXT 0x00000002u +#define CTL_ALLCAPTURES 0x00000004u +#define CTL_ALLUSEDTEXT 0x00000008u +#define CTL_ALTGLOBAL 0x00000010u +#define CTL_BINCODE 0x00000020u +#define CTL_CALLOUT_CAPTURE 0x00000040u +#define CTL_CALLOUT_INFO 0x00000080u +#define CTL_CALLOUT_NONE 0x00000100u +#define CTL_DFA 0x00000200u +#define CTL_EXPAND 0x00000400u +#define CTL_FINDLIMITS 0x00000800u +#define CTL_FULLBINCODE 0x00001000u +#define CTL_GETALL 0x00002000u +#define CTL_GLOBAL 0x00004000u +#define CTL_HEXPAT 0x00008000u +#define CTL_INFO 0x00010000u +#define CTL_JITFAST 0x00020000u +#define CTL_JITVERIFY 0x00040000u +#define CTL_MARK 0x00080000u +#define CTL_MEMORY 0x00100000u +#define CTL_NULLCONTEXT 0x00200000u +#define CTL_POSIX 0x00400000u +#define CTL_PUSH 0x00800000u +#define CTL_STARTCHAR 0x01000000u +#define CTL_ZERO_TERMINATE 0x02000000u +/* Spare 0x04000000u */ +/* Spare 0x08000000u */ +/* Spare 0x10000000u */ +/* Spare 0x20000000u */ +#define CTL_NL_SET 0x40000000u /* Informational */ +#define CTL_BSR_SET 0x80000000u /* Informational */ -#define CTL_BSR_SET 0x80000000u /* This is informational */ -#define CTL_NL_SET 0x40000000u /* This is informational */ +/* Second control word */ + +#define CTL2_SUBSTITUTE_EXTENDED 0x00000001u +#define CTL2_SUBSTITUTE_OVERFLOW_LENGTH 0x00000002u +#define CTL2_SUBSTITUTE_UNKNOWN_UNSET 0x00000004u +#define CTL2_SUBSTITUTE_UNSET_EMPTY 0x00000008u + +/* Combinations */ #define CTL_DEBUG (CTL_FULLBINCODE|CTL_INFO) /* For setting */ #define CTL_ANYINFO (CTL_DEBUG|CTL_BINCODE|CTL_CALLOUT_INFO) @@ -420,6 +461,11 @@ data line. */ CTL_MEMORY|\ CTL_STARTCHAR) +#define CTL2_ALLPD (CTL2_SUBSTITUTE_EXTENDED|\ + CTL2_SUBSTITUTE_OVERFLOW_LENGTH|\ + CTL2_SUBSTITUTE_UNKNOWN_UNSET|\ + CTL2_SUBSTITUTE_UNSET_EMPTY) + /* Structures for holding modifier information for patterns and subject strings (data). Fields containing modifiers that can be set either for a pattern or a subject must be at the start and in the same order in both cases so that the @@ -428,10 +474,12 @@ same offset in the big table below works for both. */ typedef struct patctl { /* Structure for pattern modifiers. */ uint32_t options; /* Must be in same position as datctl */ uint32_t control; /* Must be in same position as datctl */ + uint32_t control2; /* Must be in same position as datctl */ uint8_t replacement[REPLACE_MODSIZE]; /* So must this */ uint32_t jit; uint32_t stackguard_test; uint32_t tables_id; + uint32_t regerror_buffsize; uint8_t locale[LOCALESIZE]; } patctl; @@ -441,6 +489,7 @@ typedef struct patctl { /* Structure for pattern modifiers. */ typedef struct datctl { /* Structure for data line modifiers. */ uint32_t options; /* Must be in same position as patctl */ uint32_t control; /* Must be in same position as patctl */ + uint32_t control2; /* Must be in same position as patctl */ uint8_t replacement[REPLACE_MODSIZE]; /* So must this */ uint32_t cfail[2]; int32_t callout_data; @@ -481,82 +530,94 @@ typedef struct modstruct { } modstruct; static modstruct modlist[] = { - { "aftertext", MOD_PNDP, MOD_CTL, CTL_AFTERTEXT, PO(control) }, - { "allaftertext", MOD_PNDP, MOD_CTL, CTL_ALLAFTERTEXT, PO(control) }, - { "allcaptures", MOD_PND, MOD_CTL, CTL_ALLCAPTURES, PO(control) }, - { "allow_empty_class", MOD_PAT, MOD_OPT, PCRE2_ALLOW_EMPTY_CLASS, PO(options) }, - { "allusedtext", MOD_PNDP, MOD_CTL, CTL_ALLUSEDTEXT, PO(control) }, - { "alt_bsux", MOD_PAT, MOD_OPT, PCRE2_ALT_BSUX, PO(options) }, - { "alt_circumflex", MOD_PAT, MOD_OPT, PCRE2_ALT_CIRCUMFLEX, PO(options) }, - { "altglobal", MOD_PND, MOD_CTL, CTL_ALTGLOBAL, PO(control) }, - { "anchored", MOD_PD, MOD_OPT, PCRE2_ANCHORED, PD(options) }, - { "auto_callout", MOD_PAT, MOD_OPT, PCRE2_AUTO_CALLOUT, PO(options) }, - { "bincode", MOD_PAT, MOD_CTL, CTL_BINCODE, PO(control) }, - { "bsr", MOD_CTC, MOD_BSR, 0, CO(bsr_convention) }, - { "callout_capture", MOD_DAT, MOD_CTL, CTL_CALLOUT_CAPTURE, DO(control) }, - { "callout_data", MOD_DAT, MOD_INS, 0, DO(callout_data) }, - { "callout_fail", MOD_DAT, MOD_IN2, 0, DO(cfail) }, - { "callout_info", MOD_PAT, MOD_CTL, CTL_CALLOUT_INFO, PO(control) }, - { "callout_none", MOD_DAT, MOD_CTL, CTL_CALLOUT_NONE, DO(control) }, - { "caseless", MOD_PATP, MOD_OPT, PCRE2_CASELESS, PO(options) }, - { "copy", MOD_DAT, MOD_NN, DO(copy_numbers), DO(copy_names) }, - { "debug", MOD_PAT, MOD_CTL, CTL_DEBUG, PO(control) }, - { "dfa", MOD_DAT, MOD_CTL, CTL_DFA, DO(control) }, - { "dfa_restart", MOD_DAT, MOD_OPT, PCRE2_DFA_RESTART, DO(options) }, - { "dfa_shortest", MOD_DAT, MOD_OPT, PCRE2_DFA_SHORTEST, DO(options) }, - { "dollar_endonly", MOD_PAT, MOD_OPT, PCRE2_DOLLAR_ENDONLY, PO(options) }, - { "dotall", MOD_PATP, MOD_OPT, PCRE2_DOTALL, PO(options) }, - { "dupnames", MOD_PATP, MOD_OPT, PCRE2_DUPNAMES, PO(options) }, - { "extended", MOD_PATP, MOD_OPT, PCRE2_EXTENDED, PO(options) }, - { "find_limits", MOD_DAT, MOD_CTL, CTL_FINDLIMITS, DO(control) }, - { "firstline", MOD_PAT, MOD_OPT, PCRE2_FIRSTLINE, PO(options) }, - { "fullbincode", MOD_PAT, MOD_CTL, CTL_FULLBINCODE, PO(control) }, - { "get", MOD_DAT, MOD_NN, DO(get_numbers), DO(get_names) }, - { "getall", MOD_DAT, MOD_CTL, CTL_GETALL, DO(control) }, - { "global", MOD_PNDP, MOD_CTL, CTL_GLOBAL, PO(control) }, - { "hex", MOD_PAT, MOD_CTL, CTL_HEXPAT, PO(control) }, - { "info", MOD_PAT, MOD_CTL, CTL_INFO, PO(control) }, - { "jit", MOD_PAT, MOD_IND, 7, PO(jit) }, - { "jitfast", MOD_PAT, MOD_CTL, CTL_JITFAST, PO(control) }, - { "jitstack", MOD_DAT, MOD_INT, 0, DO(jitstack) }, - { "jitverify", MOD_PAT, MOD_CTL, CTL_JITVERIFY, PO(control) }, - { "locale", MOD_PAT, MOD_STR, LOCALESIZE, PO(locale) }, - { "mark", MOD_PNDP, MOD_CTL, CTL_MARK, PO(control) }, - { "match_limit", MOD_CTM, MOD_INT, 0, MO(match_limit) }, - { "match_unset_backref", MOD_PAT, MOD_OPT, PCRE2_MATCH_UNSET_BACKREF, PO(options) }, - { "memory", MOD_PD, MOD_CTL, CTL_MEMORY, PD(control) }, - { "multiline", MOD_PATP, MOD_OPT, PCRE2_MULTILINE, PO(options) }, - { "never_backslash_c", MOD_PAT, MOD_OPT, PCRE2_NEVER_BACKSLASH_C, PO(options) }, - { "never_ucp", MOD_PAT, MOD_OPT, PCRE2_NEVER_UCP, PO(options) }, - { "never_utf", MOD_PAT, MOD_OPT, PCRE2_NEVER_UTF, PO(options) }, - { "newline", MOD_CTC, MOD_NL, 0, CO(newline_convention) }, - { "no_auto_capture", MOD_PAT, MOD_OPT, PCRE2_NO_AUTO_CAPTURE, PO(options) }, - { "no_auto_possess", MOD_PATP, MOD_OPT, PCRE2_NO_AUTO_POSSESS, PO(options) }, - { "no_dotstar_anchor", MOD_PAT, MOD_OPT, PCRE2_NO_DOTSTAR_ANCHOR, PO(options) }, - { "no_start_optimize", MOD_PATP, MOD_OPT, PCRE2_NO_START_OPTIMIZE, PO(options) }, - { "no_utf_check", MOD_PD, MOD_OPT, PCRE2_NO_UTF_CHECK, PD(options) }, - { "notbol", MOD_DAT, MOD_OPT, PCRE2_NOTBOL, DO(options) }, - { "notempty", MOD_DAT, MOD_OPT, PCRE2_NOTEMPTY, DO(options) }, - { "notempty_atstart", MOD_DAT, MOD_OPT, PCRE2_NOTEMPTY_ATSTART, DO(options) }, - { "noteol", MOD_DAT, MOD_OPT, PCRE2_NOTEOL, DO(options) }, - { "offset", MOD_DAT, MOD_INT, 0, DO(offset) }, - { "ovector", MOD_DAT, MOD_INT, 0, DO(oveccount) }, - { "parens_nest_limit", MOD_CTC, MOD_INT, 0, CO(parens_nest_limit) }, - { "partial_hard", MOD_DAT, MOD_OPT, PCRE2_PARTIAL_HARD, DO(options) }, - { "partial_soft", MOD_DAT, MOD_OPT, PCRE2_PARTIAL_SOFT, DO(options) }, - { "ph", MOD_DAT, MOD_OPT, PCRE2_PARTIAL_HARD, DO(options) }, - { "posix", MOD_PAT, MOD_CTL, CTL_POSIX, PO(control) }, - { "ps", MOD_DAT, MOD_OPT, PCRE2_PARTIAL_SOFT, DO(options) }, - { "push", MOD_PAT, MOD_CTL, CTL_PUSH, PO(control) }, - { "recursion_limit", MOD_CTM, MOD_INT, 0, MO(recursion_limit) }, - { "replace", MOD_PND, MOD_STR, REPLACE_MODSIZE, PO(replacement) }, - { "stackguard", MOD_PAT, MOD_INT, 0, PO(stackguard_test) }, - { "startchar", MOD_PND, MOD_CTL, CTL_STARTCHAR, PO(control) }, - { "tables", MOD_PAT, MOD_INT, 0, PO(tables_id) }, - { "ucp", MOD_PATP, MOD_OPT, PCRE2_UCP, PO(options) }, - { "ungreedy", MOD_PAT, MOD_OPT, PCRE2_UNGREEDY, PO(options) }, - { "utf", MOD_PATP, MOD_OPT, PCRE2_UTF, PO(options) }, - { "zero_terminate", MOD_DAT, MOD_CTL, CTL_ZERO_TERMINATE, DO(control) } + { "aftertext", MOD_PNDP, MOD_CTL, CTL_AFTERTEXT, PO(control) }, + { "allaftertext", MOD_PNDP, MOD_CTL, CTL_ALLAFTERTEXT, PO(control) }, + { "allcaptures", MOD_PND, MOD_CTL, CTL_ALLCAPTURES, PO(control) }, + { "allow_empty_class", MOD_PAT, MOD_OPT, PCRE2_ALLOW_EMPTY_CLASS, PO(options) }, + { "allusedtext", MOD_PNDP, MOD_CTL, CTL_ALLUSEDTEXT, PO(control) }, + { "alt_bsux", MOD_PAT, MOD_OPT, PCRE2_ALT_BSUX, PO(options) }, + { "alt_circumflex", MOD_PAT, MOD_OPT, PCRE2_ALT_CIRCUMFLEX, PO(options) }, + { "alt_verbnames", MOD_PAT, MOD_OPT, PCRE2_ALT_VERBNAMES, PO(options) }, + { "altglobal", MOD_PND, MOD_CTL, CTL_ALTGLOBAL, PO(control) }, + { "anchored", MOD_PD, MOD_OPT, PCRE2_ANCHORED, PD(options) }, + { "auto_callout", MOD_PAT, MOD_OPT, PCRE2_AUTO_CALLOUT, PO(options) }, + { "bincode", MOD_PAT, MOD_CTL, CTL_BINCODE, PO(control) }, + { "bsr", MOD_CTC, MOD_BSR, 0, CO(bsr_convention) }, + { "callout_capture", MOD_DAT, MOD_CTL, CTL_CALLOUT_CAPTURE, DO(control) }, + { "callout_data", MOD_DAT, MOD_INS, 0, DO(callout_data) }, + { "callout_fail", MOD_DAT, MOD_IN2, 0, DO(cfail) }, + { "callout_info", MOD_PAT, MOD_CTL, CTL_CALLOUT_INFO, PO(control) }, + { "callout_none", MOD_DAT, MOD_CTL, CTL_CALLOUT_NONE, DO(control) }, + { "caseless", MOD_PATP, MOD_OPT, PCRE2_CASELESS, PO(options) }, + { "copy", MOD_DAT, MOD_NN, DO(copy_numbers), DO(copy_names) }, + { "debug", MOD_PAT, MOD_CTL, CTL_DEBUG, PO(control) }, + { "dfa", MOD_DAT, MOD_CTL, CTL_DFA, DO(control) }, + { "dfa_restart", MOD_DAT, MOD_OPT, PCRE2_DFA_RESTART, DO(options) }, + { "dfa_shortest", MOD_DAT, MOD_OPT, PCRE2_DFA_SHORTEST, DO(options) }, + { "dollar_endonly", MOD_PAT, MOD_OPT, PCRE2_DOLLAR_ENDONLY, PO(options) }, + { "dotall", MOD_PATP, MOD_OPT, PCRE2_DOTALL, PO(options) }, + { "dupnames", MOD_PATP, MOD_OPT, PCRE2_DUPNAMES, PO(options) }, + { "expand", MOD_PAT, MOD_CTL, CTL_EXPAND, PO(control) }, + { "extended", MOD_PATP, MOD_OPT, PCRE2_EXTENDED, PO(options) }, + { "find_limits", MOD_DAT, MOD_CTL, CTL_FINDLIMITS, DO(control) }, + { "firstline", MOD_PAT, MOD_OPT, PCRE2_FIRSTLINE, PO(options) }, + { "fullbincode", MOD_PAT, MOD_CTL, CTL_FULLBINCODE, PO(control) }, + { "get", MOD_DAT, MOD_NN, DO(get_numbers), DO(get_names) }, + { "getall", MOD_DAT, MOD_CTL, CTL_GETALL, DO(control) }, + { "global", MOD_PNDP, MOD_CTL, CTL_GLOBAL, PO(control) }, + { "hex", MOD_PAT, MOD_CTL, CTL_HEXPAT, PO(control) }, + { "info", MOD_PAT, MOD_CTL, CTL_INFO, PO(control) }, + { "jit", MOD_PAT, MOD_IND, 7, PO(jit) }, + { "jitfast", MOD_PAT, MOD_CTL, CTL_JITFAST, PO(control) }, + { "jitstack", MOD_DAT, MOD_INT, 0, DO(jitstack) }, + { "jitverify", MOD_PAT, MOD_CTL, CTL_JITVERIFY, PO(control) }, + { "locale", MOD_PAT, MOD_STR, LOCALESIZE, PO(locale) }, + { "mark", MOD_PNDP, MOD_CTL, CTL_MARK, PO(control) }, + { "match_limit", MOD_CTM, MOD_INT, 0, MO(match_limit) }, + { "match_unset_backref", MOD_PAT, MOD_OPT, PCRE2_MATCH_UNSET_BACKREF, PO(options) }, + { "max_pattern_length", MOD_CTC, MOD_SIZ, 0, CO(max_pattern_length) }, + { "memory", MOD_PD, MOD_CTL, CTL_MEMORY, PD(control) }, + { "multiline", MOD_PATP, MOD_OPT, PCRE2_MULTILINE, PO(options) }, + { "never_backslash_c", MOD_PAT, MOD_OPT, PCRE2_NEVER_BACKSLASH_C, PO(options) }, + { "never_ucp", MOD_PAT, MOD_OPT, PCRE2_NEVER_UCP, PO(options) }, + { "never_utf", MOD_PAT, MOD_OPT, PCRE2_NEVER_UTF, PO(options) }, + { "newline", MOD_CTC, MOD_NL, 0, CO(newline_convention) }, + { "no_auto_capture", MOD_PAT, MOD_OPT, PCRE2_NO_AUTO_CAPTURE, PO(options) }, + { "no_auto_possess", MOD_PATP, MOD_OPT, PCRE2_NO_AUTO_POSSESS, PO(options) }, + { "no_dotstar_anchor", MOD_PAT, MOD_OPT, PCRE2_NO_DOTSTAR_ANCHOR, PO(options) }, + { "no_start_optimize", MOD_PATP, MOD_OPT, PCRE2_NO_START_OPTIMIZE, PO(options) }, + { "no_utf_check", MOD_PD, MOD_OPT, PCRE2_NO_UTF_CHECK, PD(options) }, + { "notbol", MOD_DAT, MOD_OPT, PCRE2_NOTBOL, DO(options) }, + { "notempty", MOD_DAT, MOD_OPT, PCRE2_NOTEMPTY, DO(options) }, + { "notempty_atstart", MOD_DAT, MOD_OPT, PCRE2_NOTEMPTY_ATSTART, DO(options) }, + { "noteol", MOD_DAT, MOD_OPT, PCRE2_NOTEOL, DO(options) }, + { "null_context", MOD_PD, MOD_CTL, CTL_NULLCONTEXT, PO(control) }, + { "offset", MOD_DAT, MOD_INT, 0, DO(offset) }, + { "offset_limit", MOD_CTM, MOD_SIZ, 0, MO(offset_limit)}, + { "ovector", MOD_DAT, MOD_INT, 0, DO(oveccount) }, + { "parens_nest_limit", MOD_CTC, MOD_INT, 0, CO(parens_nest_limit) }, + { "partial_hard", MOD_DAT, MOD_OPT, PCRE2_PARTIAL_HARD, DO(options) }, + { "partial_soft", MOD_DAT, MOD_OPT, PCRE2_PARTIAL_SOFT, DO(options) }, + { "ph", MOD_DAT, MOD_OPT, PCRE2_PARTIAL_HARD, DO(options) }, + { "posix", MOD_PAT, MOD_CTL, CTL_POSIX, PO(control) }, + { "ps", MOD_DAT, MOD_OPT, PCRE2_PARTIAL_SOFT, DO(options) }, + { "push", MOD_PAT, MOD_CTL, CTL_PUSH, PO(control) }, + { "recursion_limit", MOD_CTM, MOD_INT, 0, MO(recursion_limit) }, + { "regerror_buffsize", MOD_PAT, MOD_INT, 0, PO(regerror_buffsize) }, + { "replace", MOD_PND, MOD_STR, REPLACE_MODSIZE, PO(replacement) }, + { "stackguard", MOD_PAT, MOD_INT, 0, PO(stackguard_test) }, + { "startchar", MOD_PND, MOD_CTL, CTL_STARTCHAR, PO(control) }, + { "startoffset", MOD_DAT, MOD_INT, 0, DO(offset) }, + { "substitute_extended", MOD_PND, MOD_CTL, CTL2_SUBSTITUTE_EXTENDED, PO(control2) }, + { "substitute_overflow_length", MOD_PND, MOD_CTL, CTL2_SUBSTITUTE_OVERFLOW_LENGTH, PO(control2) }, + { "substitute_unknown_unset", MOD_PND, MOD_CTL, CTL2_SUBSTITUTE_UNKNOWN_UNSET, PO(control2) }, + { "substitute_unset_empty", MOD_PND, MOD_CTL, CTL2_SUBSTITUTE_UNSET_EMPTY, PO(control2) }, + { "tables", MOD_PAT, MOD_INT, 0, PO(tables_id) }, + { "ucp", MOD_PATP, MOD_OPT, PCRE2_UCP, PO(options) }, + { "ungreedy", MOD_PAT, MOD_OPT, PCRE2_UNGREEDY, PO(options) }, + { "use_offset_limit", MOD_PAT, MOD_OPT, PCRE2_USE_OFFSET_LIMIT, PO(options) }, + { "utf", MOD_PATP, MOD_OPT, PCRE2_UTF, PO(options) }, + { "zero_terminate", MOD_DAT, MOD_CTL, CTL_ZERO_TERMINATE, DO(control) } }; #define MODLISTCOUNT sizeof(modlist)/sizeof(modstruct) @@ -568,16 +629,15 @@ static modstruct modlist[] = { PCRE2_UCP|PCRE2_UTF|PCRE2_UNGREEDY) #define POSIX_SUPPORTED_COMPILE_CONTROLS ( \ - CTL_AFTERTEXT|CTL_ALLAFTERTEXT|CTL_POSIX) + CTL_AFTERTEXT|CTL_ALLAFTERTEXT|CTL_EXPAND|CTL_POSIX) + +#define POSIX_SUPPORTED_COMPILE_CONTROLS2 (0) #define POSIX_SUPPORTED_MATCH_OPTIONS ( \ PCRE2_NOTBOL|PCRE2_NOTEMPTY|PCRE2_NOTEOL) -#define POSIX_SUPPORTED_MATCH_CONTROLS (CTL_AFTERTEXT|CTL_ALLAFTERTEXT) - -/* Controls that are mutually exclusive. */ - -#define EXCLUSIVE_DAT_CONTROLS (CTL_ALLUSEDTEXT|CTL_STARTCHAR) +#define POSIX_SUPPORTED_MATCH_CONTROLS (CTL_AFTERTEXT|CTL_ALLAFTERTEXT) +#define POSIX_SUPPORTED_MATCH_CONTROLS2 (0) /* Control bits that are not ignored with 'push'. */ @@ -585,14 +645,31 @@ static modstruct modlist[] = { CTL_BINCODE|CTL_CALLOUT_INFO|CTL_FULLBINCODE|CTL_HEXPAT|CTL_INFO| \ CTL_JITVERIFY|CTL_MEMORY|CTL_PUSH|CTL_BSR_SET|CTL_NL_SET) +#define PUSH_SUPPORTED_COMPILE_CONTROLS2 (0) + /* Controls that apply only at compile time with 'push'. */ -#define PUSH_COMPILE_ONLY_CONTROLS CTL_JITVERIFY +#define PUSH_COMPILE_ONLY_CONTROLS CTL_JITVERIFY +#define PUSH_COMPILE_ONLY_CONTROLS2 (0) /* Controls that are forbidden with #pop. */ #define NOTPOP_CONTROLS (CTL_HEXPAT|CTL_POSIX|CTL_PUSH) +/* Pattern controls that are mutually exclusive. At present these are all in +the first control word. */ + +static uint32_t exclusive_pat_controls[] = { + CTL_POSIX | CTL_HEXPAT, + CTL_POSIX | CTL_PUSH, + CTL_EXPAND | CTL_HEXPAT }; + +/* Data controls that are mutually exclusive. At present these are all in the +first control word. */ +static uint32_t exclusive_dat_controls[] = { + CTL_ALLUSEDTEXT | CTL_STARTCHAR, + CTL_FINDLIMITS | CTL_NULLCONTEXT }; + /* Table of single-character abbreviated modifiers. The index field is initialized to -1, but the first time the modifier is encountered, it is filled in with the index of the full entry in modlist, to save repeated searching when @@ -648,6 +725,12 @@ table itself easier to read. */ #define EBCDIC_NL 0 #endif +#ifdef NEVER_BACKSLASH_C +#define BACKSLASH_C 0 +#else +#define BACKSLASH_C 1 +#endif + typedef struct coptstruct { const char *name; uint32_t type; @@ -662,16 +745,17 @@ enum { CONF_BSR, }; static coptstruct coptlist[] = { - { "bsr", CONF_BSR, PCRE2_CONFIG_BSR }, - { "ebcdic", CONF_FIX, SUPPORT_EBCDIC }, - { "ebcdic-nl", CONF_FIZ, EBCDIC_NL }, - { "jit", CONF_INT, PCRE2_CONFIG_JIT }, - { "linksize", CONF_INT, PCRE2_CONFIG_LINKSIZE }, - { "newline", CONF_NL, PCRE2_CONFIG_NEWLINE }, - { "pcre2-16", CONF_FIX, SUPPORT_16 }, - { "pcre2-32", CONF_FIX, SUPPORT_32 }, - { "pcre2-8", CONF_FIX, SUPPORT_8 }, - { "unicode", CONF_INT, PCRE2_CONFIG_UNICODE } + { "backslash-C", CONF_FIX, BACKSLASH_C }, + { "bsr", CONF_BSR, PCRE2_CONFIG_BSR }, + { "ebcdic", CONF_FIX, SUPPORT_EBCDIC }, + { "ebcdic-nl", CONF_FIZ, EBCDIC_NL }, + { "jit", CONF_INT, PCRE2_CONFIG_JIT }, + { "linksize", CONF_INT, PCRE2_CONFIG_LINKSIZE }, + { "newline", CONF_NL, PCRE2_CONFIG_NEWLINE }, + { "pcre2-16", CONF_FIX, SUPPORT_16 }, + { "pcre2-32", CONF_FIX, SUPPORT_32 }, + { "pcre2-8", CONF_FIX, SUPPORT_8 }, + { "unicode", CONF_INT, PCRE2_CONFIG_UNICODE } }; #define COPTLISTCOUNT sizeof(coptlist)/sizeof(coptstruct) @@ -697,6 +781,7 @@ static BOOL restrict_for_perl_test = FALSE; static BOOL show_memory = FALSE; static int code_unit_size; /* Bytes */ +static int jitrc; /* Return from JIT compile */ static int test_mode = DEFAULT_TEST_MODE; static int timeit = 0; static int timeitm = 0; @@ -711,6 +796,8 @@ static uint32_t maxlookbehind; static uint32_t max_oveccount; static uint32_t callout_count; +static uint16_t local_newline_default = 0; + static VERSION_TYPE jittarget[VERSION_SIZE]; static VERSION_TYPE version[VERSION_SIZE]; static VERSION_TYPE uversion[VERSION_SIZE]; @@ -737,7 +824,7 @@ buffer is where all input lines are read. Its size is the same as pbuffer8. Pattern lines are always copied to pbuffer8 for use in callouts, even if they are actually compiled from pbuffer16 or pbuffer32. */ -static int pbuffer8_size = 50000; /* Initial size, bytes */ +static size_t pbuffer8_size = 50000; /* Initial size, bytes */ static uint8_t *pbuffer8 = NULL; static uint8_t *buffer = NULL; @@ -858,19 +945,19 @@ are supported. */ #define PCRE2_COMPILE(a,b,c,d,e,f,g) \ if (test_mode == PCRE8_MODE) \ - G(a,8) = pcre2_compile_8(G(b,8),c,d,e,f,G(g,8)); \ + G(a,8) = pcre2_compile_8(G(b,8),c,d,e,f,g); \ else if (test_mode == PCRE16_MODE) \ - G(a,16) = pcre2_compile_16(G(b,16),c,d,e,f,G(g,16)); \ + G(a,16) = pcre2_compile_16(G(b,16),c,d,e,f,g); \ else \ - G(a,32) = pcre2_compile_32(G(b,32),c,d,e,f,G(g,32)) + G(a,32) = pcre2_compile_32(G(b,32),c,d,e,f,g) #define PCRE2_DFA_MATCH(a,b,c,d,e,f,g,h,i,j) \ if (test_mode == PCRE8_MODE) \ - a = pcre2_dfa_match_8(G(b,8),(PCRE2_SPTR8)c,d,e,f,G(g,8),G(h,8),i,j); \ + a = pcre2_dfa_match_8(G(b,8),(PCRE2_SPTR8)c,d,e,f,G(g,8),h,i,j); \ else if (test_mode == PCRE16_MODE) \ - a = pcre2_dfa_match_16(G(b,16),(PCRE2_SPTR16)c,d,e,f,G(g,16),G(h,16),i,j); \ + a = pcre2_dfa_match_16(G(b,16),(PCRE2_SPTR16)c,d,e,f,G(g,16),h,i,j); \ else \ - a = pcre2_dfa_match_32(G(b,32),(PCRE2_SPTR32)c,d,e,f,G(g,32),G(h,32),i,j) + a = pcre2_dfa_match_32(G(b,32),(PCRE2_SPTR32)c,d,e,f,G(g,32),h,i,j) #define PCRE2_GET_ERROR_MESSAGE(r,a,b) \ if (test_mode == PCRE8_MODE) \ @@ -896,10 +983,10 @@ are supported. */ else \ a = pcre2_get_startchar_32(G(b,32)) -#define PCRE2_JIT_COMPILE(a,b) \ - if (test_mode == PCRE8_MODE) pcre2_jit_compile_8(G(a,8),b); \ - else if (test_mode == PCRE16_MODE) pcre2_jit_compile_16(G(a,16),b); \ - else pcre2_jit_compile_32(G(a,32),b) +#define PCRE2_JIT_COMPILE(r,a,b) \ + if (test_mode == PCRE8_MODE) r = pcre2_jit_compile_8(G(a,8),b); \ + else if (test_mode == PCRE16_MODE) r = pcre2_jit_compile_16(G(a,16),b); \ + else r = pcre2_jit_compile_32(G(a,32),b) #define PCRE2_JIT_FREE_UNUSED_MEMORY(a) \ if (test_mode == PCRE8_MODE) pcre2_jit_free_unused_memory_8(G(a,8)); \ @@ -908,11 +995,11 @@ are supported. */ #define PCRE2_JIT_MATCH(a,b,c,d,e,f,g,h) \ if (test_mode == PCRE8_MODE) \ - a = pcre2_jit_match_8(G(b,8),(PCRE2_SPTR8)c,d,e,f,G(g,8),G(h,8)); \ + a = pcre2_jit_match_8(G(b,8),(PCRE2_SPTR8)c,d,e,f,G(g,8),h); \ else if (test_mode == PCRE16_MODE) \ - a = pcre2_jit_match_16(G(b,16),(PCRE2_SPTR16)c,d,e,f,G(g,16),G(h,16)); \ + a = pcre2_jit_match_16(G(b,16),(PCRE2_SPTR16)c,d,e,f,G(g,16),h); \ else \ - a = pcre2_jit_match_32(G(b,32),(PCRE2_SPTR32)c,d,e,f,G(g,32),G(h,32)) + a = pcre2_jit_match_32(G(b,32),(PCRE2_SPTR32)c,d,e,f,G(g,32),h) #define PCRE2_JIT_STACK_CREATE(a,b,c,d) \ if (test_mode == PCRE8_MODE) \ @@ -945,11 +1032,11 @@ are supported. */ #define PCRE2_MATCH(a,b,c,d,e,f,g,h) \ if (test_mode == PCRE8_MODE) \ - a = pcre2_match_8(G(b,8),(PCRE2_SPTR8)c,d,e,f,G(g,8),G(h,8)); \ + a = pcre2_match_8(G(b,8),(PCRE2_SPTR8)c,d,e,f,G(g,8),h); \ else if (test_mode == PCRE16_MODE) \ - a = pcre2_match_16(G(b,16),(PCRE2_SPTR16)c,d,e,f,G(g,16),G(h,16)); \ + a = pcre2_match_16(G(b,16),(PCRE2_SPTR16)c,d,e,f,G(g,16),h); \ else \ - a = pcre2_match_32(G(b,32),(PCRE2_SPTR32)c,d,e,f,G(g,32),G(h,32)) + a = pcre2_match_32(G(b,32),(PCRE2_SPTR32)c,d,e,f,G(g,32),h) #define PCRE2_MATCH_DATA_CREATE(a,b,c) \ if (test_mode == PCRE8_MODE) \ @@ -1055,6 +1142,22 @@ are supported. */ else \ pcre2_set_match_limit_32(G(a,32),b) +#define PCRE2_SET_MAX_PATTERN_LENGTH(a,b) \ + if (test_mode == PCRE8_MODE) \ + pcre2_set_max_pattern_length_8(G(a,8),b); \ + else if (test_mode == PCRE16_MODE) \ + pcre2_set_max_pattern_length_16(G(a,16),b); \ + else \ + pcre2_set_max_pattern_length_32(G(a,32),b) + +#define PCRE2_SET_OFFSET_LIMIT(a,b) \ + if (test_mode == PCRE8_MODE) \ + pcre2_set_offset_limit_8(G(a,8),b); \ + else if (test_mode == PCRE16_MODE) \ + pcre2_set_offset_limit_16(G(a,16),b); \ + else \ + pcre2_set_offset_limit_32(G(a,32),b) + #define PCRE2_SET_PARENS_NEST_LIMIT(a,b) \ if (test_mode == PCRE8_MODE) \ pcre2_set_parens_nest_limit_8(G(a,8),b); \ @@ -1293,17 +1396,17 @@ the three different cases. */ #define PCRE2_COMPILE(a,b,c,d,e,f,g) \ if (test_mode == G(G(PCRE,BITONE),_MODE)) \ - G(a,BITONE) = G(pcre2_compile_,BITONE)(G(b,BITONE),c,d,e,f,G(g,BITONE)); \ + G(a,BITONE) = G(pcre2_compile_,BITONE)(G(b,BITONE),c,d,e,f,g); \ else \ - G(a,BITTWO) = G(pcre2_compile_,BITTWO)(G(b,BITTWO),c,d,e,f,G(g,BITTWO)) + G(a,BITTWO) = G(pcre2_compile_,BITTWO)(G(b,BITTWO),c,d,e,f,g) #define PCRE2_DFA_MATCH(a,b,c,d,e,f,g,h,i,j) \ if (test_mode == G(G(PCRE,BITONE),_MODE)) \ a = G(pcre2_dfa_match_,BITONE)(G(b,BITONE),(G(PCRE2_SPTR,BITONE))c,d,e,f, \ - G(g,BITONE),G(h,BITONE),i,j); \ + G(g,BITONE),h,i,j); \ else \ a = G(pcre2_dfa_match_,BITTWO)(G(b,BITTWO),(G(PCRE2_SPTR,BITTWO))c,d,e,f, \ - G(g,BITTWO),G(h,BITTWO),i,j) + G(g,BITTWO),h,i,j) #define PCRE2_GET_ERROR_MESSAGE(r,a,b) \ if (test_mode == G(G(PCRE,BITONE),_MODE)) \ @@ -1323,11 +1426,11 @@ the three different cases. */ else \ a = G(pcre2_get_startchar_,BITTWO)(G(b,BITTWO)) -#define PCRE2_JIT_COMPILE(a,b) \ +#define PCRE2_JIT_COMPILE(r,a,b) \ if (test_mode == G(G(PCRE,BITONE),_MODE)) \ - G(pcre2_jit_compile_,BITONE)(G(a,BITONE),b); \ + r = G(pcre2_jit_compile_,BITONE)(G(a,BITONE),b); \ else \ - G(pcre2_jit_compile_,BITTWO)(G(a,BITTWO),b) + r = G(pcre2_jit_compile_,BITTWO)(G(a,BITTWO),b) #define PCRE2_JIT_FREE_UNUSED_MEMORY(a) \ if (test_mode == G(G(PCRE,BITONE),_MODE)) \ @@ -1338,10 +1441,10 @@ the three different cases. */ #define PCRE2_JIT_MATCH(a,b,c,d,e,f,g,h) \ if (test_mode == G(G(PCRE,BITONE),_MODE)) \ a = G(pcre2_jit_match_,BITONE)(G(b,BITONE),(G(PCRE2_SPTR,BITONE))c,d,e,f, \ - G(g,BITONE),G(h,BITONE)); \ + G(g,BITONE),h); \ else \ a = G(pcre2_jit_match_,BITTWO)(G(b,BITTWO),(G(PCRE2_SPTR,BITTWO))c,d,e,f, \ - G(g,BITTWO),G(h,BITTWO)) + G(g,BITTWO),h) #define PCRE2_JIT_STACK_CREATE(a,b,c,d) \ if (test_mode == G(G(PCRE,BITONE),_MODE)) \ @@ -1370,10 +1473,10 @@ the three different cases. */ #define PCRE2_MATCH(a,b,c,d,e,f,g,h) \ if (test_mode == G(G(PCRE,BITONE),_MODE)) \ a = G(pcre2_match_,BITONE)(G(b,BITONE),(G(PCRE2_SPTR,BITONE))c,d,e,f, \ - G(g,BITONE),G(h,BITONE)); \ + G(g,BITONE),h); \ else \ a = G(pcre2_match_,BITTWO)(G(b,BITTWO),(G(PCRE2_SPTR,BITTWO))c,d,e,f, \ - G(g,BITTWO),G(h,BITTWO)) + G(g,BITTWO),h) #define PCRE2_MATCH_DATA_CREATE(a,b,c) \ if (test_mode == G(G(PCRE,BITONE),_MODE)) \ @@ -1455,6 +1558,18 @@ the three different cases. */ else \ G(pcre2_set_match_limit_,BITTWO)(G(a,BITTWO),b) +#define PCRE2_SET_MAX_PATTERN_LENGTH(a,b) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(pcre2_set_max_pattern_length_,BITONE)(G(a,BITONE),b); \ + else \ + G(pcre2_set_max_pattern_length_,BITTWO)(G(a,BITTWO),b) + +#define PCRE2_SET_OFFSET_LIMIT(a,b) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(pcre2_set_offset_limit_,BITONE)(G(a,BITONE),b); \ + else \ + G(pcre2_set_offset_limit_,BITTWO)(G(a,BITTWO),b) + #define PCRE2_SET_PARENS_NEST_LIMIT(a,b) \ if (test_mode == G(G(PCRE,BITONE),_MODE)) \ G(pcre2_set_parens_nest_limit_,BITONE)(G(a,BITONE),b); \ @@ -1615,17 +1730,17 @@ the three different cases. */ a = pcre2_callout_enumerate_8(compiled_code8, \ (int (*)(struct pcre2_callout_enumerate_block_8 *, void *))b,c) #define PCRE2_COMPILE(a,b,c,d,e,f,g) \ - G(a,8) = pcre2_compile_8(G(b,8),c,d,e,f,G(g,8)) + G(a,8) = pcre2_compile_8(G(b,8),c,d,e,f,g) #define PCRE2_DFA_MATCH(a,b,c,d,e,f,g,h,i,j) \ - a = pcre2_dfa_match_8(G(b,8),(PCRE2_SPTR8)c,d,e,f,G(g,8),G(h,8),i,j) + a = pcre2_dfa_match_8(G(b,8),(PCRE2_SPTR8)c,d,e,f,G(g,8),h,i,j) #define PCRE2_GET_ERROR_MESSAGE(r,a,b) \ r = pcre2_get_error_message_8(a,G(b,8),G(G(b,8),_size)) #define PCRE2_GET_OVECTOR_COUNT(a,b) a = pcre2_get_ovector_count_8(G(b,8)) #define PCRE2_GET_STARTCHAR(a,b) a = pcre2_get_startchar_8(G(b,8)) -#define PCRE2_JIT_COMPILE(a,b) pcre2_jit_compile_8(G(a,8),b) +#define PCRE2_JIT_COMPILE(r,a,b) r = pcre2_jit_compile_8(G(a,8),b) #define PCRE2_JIT_FREE_UNUSED_MEMORY(a) pcre2_jit_free_unused_memory_8(G(a,8)) #define PCRE2_JIT_MATCH(a,b,c,d,e,f,g,h) \ - a = pcre2_jit_match_8(G(b,8),(PCRE2_SPTR8)c,d,e,f,G(g,8),G(h,8)) + a = pcre2_jit_match_8(G(b,8),(PCRE2_SPTR8)c,d,e,f,G(g,8),h) #define PCRE2_JIT_STACK_CREATE(a,b,c,d) \ a = (PCRE2_JIT_STACK *)pcre2_jit_stack_create_8(b,c,d); #define PCRE2_JIT_STACK_ASSIGN(a,b,c) \ @@ -1633,7 +1748,7 @@ the three different cases. */ #define PCRE2_JIT_STACK_FREE(a) pcre2_jit_stack_free_8((pcre2_jit_stack_8 *)a); #define PCRE2_MAKETABLES(a) a = pcre2_maketables_8(NULL) #define PCRE2_MATCH(a,b,c,d,e,f,g,h) \ - a = pcre2_match_8(G(b,8),(PCRE2_SPTR8)c,d,e,f,G(g,8),G(h,8)) + a = pcre2_match_8(G(b,8),(PCRE2_SPTR8)c,d,e,f,G(g,8),h) #define PCRE2_MATCH_DATA_CREATE(a,b,c) G(a,8) = pcre2_match_data_create_8(b,c) #define PCRE2_MATCH_DATA_CREATE_FROM_PATTERN(a,b,c) \ G(a,8) = pcre2_match_data_create_from_pattern_8(G(b,8),c) @@ -1653,6 +1768,8 @@ the three different cases. */ #define PCRE2_SET_COMPILE_RECURSION_GUARD(a,b,c) \ pcre2_set_compile_recursion_guard_8(G(a,8),b,c) #define PCRE2_SET_MATCH_LIMIT(a,b) pcre2_set_match_limit_8(G(a,8),b) +#define PCRE2_SET_MAX_PATTERN_LENGTH(a,b) pcre2_set_max_pattern_length_8(G(a,8),b) +#define PCRE2_SET_OFFSET_LIMIT(a,b) pcre2_set_offset_limit_8(G(a,8),b) #define PCRE2_SET_PARENS_NEST_LIMIT(a,b) pcre2_set_parens_nest_limit_8(G(a,8),b) #define PCRE2_SET_RECURSION_LIMIT(a,b) pcre2_set_recursion_limit_8(G(a,8),b) #define PCRE2_SUBSTITUTE(a,b,c,d,e,f,g,h,i,j,k,l) \ @@ -1706,17 +1823,17 @@ the three different cases. */ a = pcre2_callout_enumerate_16(compiled_code16, \ (int (*)(struct pcre2_callout_enumerate_block_16 *, void *))b,c) #define PCRE2_COMPILE(a,b,c,d,e,f,g) \ - G(a,16) = pcre2_compile_16(G(b,16),c,d,e,f,G(g,16)) + G(a,16) = pcre2_compile_16(G(b,16),c,d,e,f,g) #define PCRE2_DFA_MATCH(a,b,c,d,e,f,g,h,i,j) \ - a = pcre2_dfa_match_16(G(b,16),(PCRE2_SPTR16)c,d,e,f,G(g,16),G(h,16),i,j) + a = pcre2_dfa_match_16(G(b,16),(PCRE2_SPTR16)c,d,e,f,G(g,16),h,i,j) #define PCRE2_GET_ERROR_MESSAGE(r,a,b) \ r = pcre2_get_error_message_16(a,G(b,16),G(G(b,16),_size)) #define PCRE2_GET_OVECTOR_COUNT(a,b) a = pcre2_get_ovector_count_16(G(b,16)) #define PCRE2_GET_STARTCHAR(a,b) a = pcre2_get_startchar_16(G(b,16)) -#define PCRE2_JIT_COMPILE(a,b) pcre2_jit_compile_16(G(a,16),b) +#define PCRE2_JIT_COMPILE(r,a,b) r = pcre2_jit_compile_16(G(a,16),b) #define PCRE2_JIT_FREE_UNUSED_MEMORY(a) pcre2_jit_free_unused_memory_16(G(a,16)) #define PCRE2_JIT_MATCH(a,b,c,d,e,f,g,h) \ - a = pcre2_jit_match_16(G(b,16),(PCRE2_SPTR16)c,d,e,f,G(g,16),G(h,16)) + a = pcre2_jit_match_16(G(b,16),(PCRE2_SPTR16)c,d,e,f,G(g,16),h) #define PCRE2_JIT_STACK_CREATE(a,b,c,d) \ a = (PCRE2_JIT_STACK *)pcre2_jit_stack_create_16(b,c,d); #define PCRE2_JIT_STACK_ASSIGN(a,b,c) \ @@ -1724,7 +1841,7 @@ the three different cases. */ #define PCRE2_JIT_STACK_FREE(a) pcre2_jit_stack_free_16((pcre2_jit_stack_16 *)a); #define PCRE2_MAKETABLES(a) a = pcre2_maketables_16(NULL) #define PCRE2_MATCH(a,b,c,d,e,f,g,h) \ - a = pcre2_match_16(G(b,16),(PCRE2_SPTR16)c,d,e,f,G(g,16),G(h,16)) + a = pcre2_match_16(G(b,16),(PCRE2_SPTR16)c,d,e,f,G(g,16),h) #define PCRE2_MATCH_DATA_CREATE(a,b,c) G(a,16) = pcre2_match_data_create_16(b,c) #define PCRE2_MATCH_DATA_CREATE_FROM_PATTERN(a,b,c) \ G(a,16) = pcre2_match_data_create_from_pattern_16(G(b,16),c) @@ -1744,6 +1861,8 @@ the three different cases. */ #define PCRE2_SET_COMPILE_RECURSION_GUARD(a,b,c) \ pcre2_set_compile_recursion_guard_16(G(a,16),b,c) #define PCRE2_SET_MATCH_LIMIT(a,b) pcre2_set_match_limit_16(G(a,16),b) +#define PCRE2_SET_MAX_PATTERN_LENGTH(a,b) pcre2_set_max_pattern_length_16(G(a,16),b) +#define PCRE2_SET_OFFSET_LIMIT(a,b) pcre2_set_offset_limit_16(G(a,16),b) #define PCRE2_SET_PARENS_NEST_LIMIT(a,b) pcre2_set_parens_nest_limit_16(G(a,16),b) #define PCRE2_SET_RECURSION_LIMIT(a,b) pcre2_set_recursion_limit_16(G(a,16),b) #define PCRE2_SUBSTITUTE(a,b,c,d,e,f,g,h,i,j,k,l) \ @@ -1797,17 +1916,17 @@ the three different cases. */ a = pcre2_callout_enumerate_32(compiled_code32, \ (int (*)(struct pcre2_callout_enumerate_block_32 *, void *))b,c) #define PCRE2_COMPILE(a,b,c,d,e,f,g) \ - G(a,32) = pcre2_compile_32(G(b,32),c,d,e,f,G(g,32)) + G(a,32) = pcre2_compile_32(G(b,32),c,d,e,f,g) #define PCRE2_DFA_MATCH(a,b,c,d,e,f,g,h,i,j) \ - a = pcre2_dfa_match_32(G(b,32),(PCRE2_SPTR32)c,d,e,f,G(g,32),G(h,32),i,j) + a = pcre2_dfa_match_32(G(b,32),(PCRE2_SPTR32)c,d,e,f,G(g,32),h,i,j) #define PCRE2_GET_ERROR_MESSAGE(r,a,b) \ r = pcre2_get_error_message_32(a,G(b,32),G(G(b,32),_size)) #define PCRE2_GET_OVECTOR_COUNT(a,b) a = pcre2_get_ovector_count_32(G(b,32)) #define PCRE2_GET_STARTCHAR(a,b) a = pcre2_get_startchar_32(G(b,32)) -#define PCRE2_JIT_COMPILE(a,b) pcre2_jit_compile_32(G(a,32),b) +#define PCRE2_JIT_COMPILE(r,a,b) r = pcre2_jit_compile_32(G(a,32),b) #define PCRE2_JIT_FREE_UNUSED_MEMORY(a) pcre2_jit_free_unused_memory_32(G(a,32)) #define PCRE2_JIT_MATCH(a,b,c,d,e,f,g,h) \ - a = pcre2_jit_match_32(G(b,32),(PCRE2_SPTR32)c,d,e,f,G(g,32),G(h,32)) + a = pcre2_jit_match_32(G(b,32),(PCRE2_SPTR32)c,d,e,f,G(g,32),h) #define PCRE2_JIT_STACK_CREATE(a,b,c,d) \ a = (PCRE2_JIT_STACK *)pcre2_jit_stack_create_32(b,c,d); #define PCRE2_JIT_STACK_ASSIGN(a,b,c) \ @@ -1815,7 +1934,7 @@ the three different cases. */ #define PCRE2_JIT_STACK_FREE(a) pcre2_jit_stack_free_32((pcre2_jit_stack_32 *)a); #define PCRE2_MAKETABLES(a) a = pcre2_maketables_32(NULL) #define PCRE2_MATCH(a,b,c,d,e,f,g,h) \ - a = pcre2_match_32(G(b,32),(PCRE2_SPTR32)c,d,e,f,G(g,32),G(h,32)) + a = pcre2_match_32(G(b,32),(PCRE2_SPTR32)c,d,e,f,G(g,32),h) #define PCRE2_MATCH_DATA_CREATE(a,b,c) G(a,32) = pcre2_match_data_create_32(b,c) #define PCRE2_MATCH_DATA_CREATE_FROM_PATTERN(a,b,c) \ G(a,32) = pcre2_match_data_create_from_pattern_32(G(b,32),c) @@ -1835,6 +1954,8 @@ the three different cases. */ #define PCRE2_SET_COMPILE_RECURSION_GUARD(a,b,c) \ pcre2_set_compile_recursion_guard_32(G(a,32),b,c) #define PCRE2_SET_MATCH_LIMIT(a,b) pcre2_set_match_limit_32(G(a,32),b) +#define PCRE2_SET_MAX_PATTERN_LENGTH(a,b) pcre2_set_max_pattern_length_32(G(a,32),b) +#define PCRE2_SET_OFFSET_LIMIT(a,b) pcre2_set_offset_limit_32(G(a,32),b) #define PCRE2_SET_PARENS_NEST_LIMIT(a,b) pcre2_set_parens_nest_limit_32(G(a,32),b) #define PCRE2_SET_RECURSION_LIMIT(a,b) pcre2_set_recursion_limit_32(G(a,32),b) #define PCRE2_SUBSTITUTE(a,b,c,d,e,f,g,h,i,j,k,l) \ @@ -2424,13 +2545,14 @@ return (int)(pp - p); *************************************************/ /* Must handle UTF-8 strings in utf8 mode. Yields number of characters printed. -If handed a NULL file, just counts chars without printing. */ +For printing *MARK strings, a negative length is given. If handed a NULL file, +just counts chars without printing. */ static int pchars8(PCRE2_SPTR8 p, int length, BOOL utf, FILE *f) { uint32_t c = 0; int yield = 0; -if (length < 0) length = strlen((char *)p); +if (length < 0) length = p[-1]; while (length-- > 0) { if (utf) @@ -2458,12 +2580,13 @@ return yield; *************************************************/ /* Must handle UTF-16 strings in utf mode. Yields number of characters printed. -If handed a NULL file, just counts chars without printing. */ +For printing *MARK strings, a negative length is given. If handed a NULL file, +just counts chars without printing. */ static int pchars16(PCRE2_SPTR16 p, int length, BOOL utf, FILE *f) { int yield = 0; -if (length < 0) length = strlen16(p); +if (length < 0) length = p[-1]; while (length-- > 0) { uint32_t c = *p++ & 0xffff; @@ -2491,13 +2614,14 @@ return yield; *************************************************/ /* Must handle UTF-32 strings in utf mode. Yields number of characters printed. -If handed a NULL file, just counts chars without printing. */ +For printing *MARK strings, a negative length is given.If handed a NULL file, +just counts chars without printing. */ static int pchars32(PCRE2_SPTR32 p, int length, BOOL utf, FILE *f) { int yield = 0; (void)(utf); /* Avoid compiler warning */ -if (length < 0) length = strlen32(p); +if (length < 0) length = p[-1]; while (length-- > 0) { uint32_t c = *p++; @@ -2598,7 +2722,7 @@ if (pbuffer16_size < 2*len + 2) pp = pbuffer16; if (!utf) { - while (len-- > 0) *pp++ = *p++; + for (; len > 0; len--) *pp++ = *p++; } else while (len > 0) { @@ -2675,7 +2799,7 @@ if (pbuffer32_size < 4*len + 4) pp = pbuffer32; if (!utf) { - while (len-- > 0) *pp++ = *p++; + for (; len > 0; len--) *pp++ = *p++; } else while (len > 0) { @@ -2715,9 +2839,8 @@ Returns: a possibly changed offset static PCRE2_SIZE backchars(uint8_t *subject, PCRE2_SIZE offset, uint32_t count, BOOL utf) { -long int yield; - -if (!utf || test_mode == PCRE32_MODE) yield = offset - count; +if (!utf || test_mode == PCRE32_MODE) + return (count >= offset)? 0 : (offset - count); else if (test_mode == PCRE8_MODE) { @@ -2727,7 +2850,7 @@ else if (test_mode == PCRE8_MODE) pp--; while ((*pp & 0xc0) == 0x80) pp--; } - yield = pp - (PCRE2_SPTR8)subject; + return pp - (PCRE2_SPTR8)subject; } else /* 16-bit mode */ @@ -2738,13 +2861,51 @@ else /* 16-bit mode */ pp--; if ((*pp & 0xfc00) == 0xdc00) pp--; } - yield = pp - (PCRE2_SPTR16)subject; + return pp - (PCRE2_SPTR16)subject; + } +} + + + +/************************************************* +* Expand input buffers * +*************************************************/ + +/* This function doubles the size of the input buffer and the buffer for +keeping an 8-bit copy of patterns (pbuffer8), and copies the current buffers to +the new ones. + +Arguments: none +Returns: nothing (aborts if malloc() fails) +*/ + +static void +expand_input_buffers(void) +{ +int new_pbuffer8_size = 2*pbuffer8_size; +uint8_t *new_buffer = (uint8_t *)malloc(new_pbuffer8_size); +uint8_t *new_pbuffer8 = (uint8_t *)malloc(new_pbuffer8_size); + +if (new_buffer == NULL || new_pbuffer8 == NULL) + { + fprintf(stderr, "pcre2test: malloc(%d) failed\n", new_pbuffer8_size); + exit(1); } -return (yield >= 0)? yield : 0; +memcpy(new_buffer, buffer, pbuffer8_size); +memcpy(new_pbuffer8, pbuffer8, pbuffer8_size); + +pbuffer8_size = new_pbuffer8_size; + +free(buffer); +free(pbuffer8); + +buffer = new_buffer; +pbuffer8 = new_pbuffer8; } + /************************************************* * Read or extend an input line * *************************************************/ @@ -2817,29 +2978,11 @@ for (;;) else { - int new_pbuffer8_size = 2*pbuffer8_size; - uint8_t *new_buffer = (uint8_t *)malloc(new_pbuffer8_size); - uint8_t *new_pbuffer8 = (uint8_t *)malloc(new_pbuffer8_size); - - if (new_buffer == NULL || new_pbuffer8 == NULL) - { - fprintf(stderr, "pcre2test: malloc(%d) failed\n", new_pbuffer8_size); - exit(1); - } - - memcpy(new_buffer, buffer, pbuffer8_size); - memcpy(new_pbuffer8, pbuffer8, pbuffer8_size); - - pbuffer8_size = new_pbuffer8_size; - - start = new_buffer + (start - buffer); - here = new_buffer + (here - buffer); - - free(buffer); - free(pbuffer8); - - buffer = new_buffer; - pbuffer8 = new_pbuffer8; + size_t start_offset = start - buffer; + size_t here_offset = here - buffer; + expand_input_buffers(); + start = buffer + start_offset; + here = buffer + here_offset; } } @@ -2874,33 +3017,6 @@ return 0; -/************************************************* -* Read number from string * -*************************************************/ - -/* We don't use strtoul() because SunOS4 doesn't have it. Rather than mess -around with conditional compilation, just do the job by hand. It is only used -for unpicking arguments, so just keep it simple. - -Arguments: - str string to be converted - endptr where to put the end pointer - -Returns: the unsigned long -*/ - -static int -get_value(const char *str, const char **endptr) -{ -int result = 0; -while(*str != 0 && isspace(*str)) str++; -while (isdigit(*str)) result = result * 10 + (int)(*str++ - '0'); -*endptr = str; -return(result); -} - - - /************************************************* * Scan the main modifier list * *************************************************/ @@ -2928,7 +3044,7 @@ while (top > bot) if (c == 0) { if (len == mlen) return mid; - c = len - mlen; + c = (int)len - (int)mlen; } if (c > 0) bot = mid + 1; else top = mid; } @@ -3050,6 +3166,8 @@ static BOOL decode_modifiers(uint8_t *p, int ctx, patctl *pctl, datctl *dctl) { uint8_t *ep, *pp; +long li; +unsigned long uli; BOOL first = TRUE; for (;;) @@ -3066,9 +3184,14 @@ for (;;) while (isspace(*p) || *p == ',') p++; if (*p == 0) break; - /* Find the end of the item. */ + /* Find the end of the item; lose trailing whitespace at end of line. */ - for (ep = p; *ep != 0 && *ep != ',' && !isspace(*ep); ep++); + for (ep = p; *ep != 0 && *ep != ','; ep++); + if (*ep == 0) + { + while (ep > p && isspace(ep[-1])) ep--; + *ep = 0; + } /* Remember if the first character is '-'. */ @@ -3210,13 +3333,34 @@ for (;;) case MOD_IN2: /* One or two unsigned integers */ if (!isdigit(*pp)) goto INVALID_VALUE; - ((uint32_t *)field)[0] = (uint32_t)strtoul((const char *)pp, &endptr, 10); + uli = strtoul((const char *)pp, &endptr, 10); + if (U32OVERFLOW(uli)) goto INVALID_VALUE; + ((uint32_t *)field)[0] = (uint32_t)uli; if (*endptr == ':') - ((uint32_t *)field)[1] = (uint32_t)strtoul((const char *)endptr+1, &endptr, 10); + { + uli = strtoul((const char *)endptr+1, &endptr, 10); + if (U32OVERFLOW(uli)) goto INVALID_VALUE; + ((uint32_t *)field)[1] = (uint32_t)uli; + } else ((uint32_t *)field)[1] = 0; pp = (uint8_t *)endptr; break; + /* PCRE2_SIZE_MAX is usually SIZE_MAX, which may be greater, equal to, or + less than ULONG_MAX. So first test for overflowing the long int, and then + test for overflowing PCRE2_SIZE_MAX if it is smaller than ULONG_MAX. */ + + case MOD_SIZ: /* PCRE2_SIZE value */ + if (!isdigit(*pp)) goto INVALID_VALUE; + uli = strtoul((const char *)pp, &endptr, 10); + if (uli == ULONG_MAX) goto INVALID_VALUE; +#if ULONG_MAX > PCRE2_SIZE_MAX + if (uli > PCRE2_SIZE_MAX) goto INVALID_VALUE; +#endif + *((PCRE2_SIZE *)field) = (PCRE2_SIZE)uli; + pp = (uint8_t *)endptr; + break; + case MOD_IND: /* Unsigned integer with default */ if (len == 0) { @@ -3227,13 +3371,17 @@ for (;;) case MOD_INT: /* Unsigned integer */ if (!isdigit(*pp)) goto INVALID_VALUE; - *((uint32_t *)field) = (uint32_t)strtoul((const char *)pp, &endptr, 10); + uli = strtoul((const char *)pp, &endptr, 10); + if (U32OVERFLOW(uli)) goto INVALID_VALUE; + *((uint32_t *)field) = (uint32_t)uli; pp = (uint8_t *)endptr; break; case MOD_INS: /* Signed integer */ if (!isdigit(*pp) && *pp != '-') goto INVALID_VALUE; - *((int32_t *)field) = (int32_t)strtol((const char *)pp, &endptr, 10); + li = strtol((const char *)pp, &endptr, 10); + if (S32OVERFLOW(li)) goto INVALID_VALUE; + *((int32_t *)field) = (int32_t)li; pp = (uint8_t *)endptr; break; @@ -3261,7 +3409,10 @@ for (;;) if (isdigit(*pp) || *pp == '-') { int ct = MAXCPYGET - 1; - int32_t value = (int32_t)strtol((const char *)pp, &endptr, 10); + int32_t value; + li = strtol((const char *)pp, &endptr, 10); + if (S32OVERFLOW(li)) goto INVALID_VALUE; + value = (int32_t)li; field = (char *)field - m->offset + m->value; /* Adjust field ptr */ if (value >= 0) /* Add new number */ { @@ -3405,15 +3556,16 @@ words. Arguments: controls control bits + controls2 more control bits before text to print before Returns: nothing */ static void -show_controls(uint32_t controls, const char *before) +show_controls(uint32_t controls, uint32_t controls2, const char *before) { -fprintf(outfile, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", +fprintf(outfile, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", before, ((controls & CTL_AFTERTEXT) != 0)? " aftertext" : "", ((controls & CTL_ALLAFTERTEXT) != 0)? " allaftertext" : "", @@ -3421,10 +3573,12 @@ fprintf(outfile, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", ((controls & CTL_ALLUSEDTEXT) != 0)? " allusedtext" : "", ((controls & CTL_ALTGLOBAL) != 0)? " altglobal" : "", ((controls & CTL_BINCODE) != 0)? " bincode" : "", + ((controls & CTL_BSR_SET) != 0)? " bsr" : "", ((controls & CTL_CALLOUT_CAPTURE) != 0)? " callout_capture" : "", ((controls & CTL_CALLOUT_INFO) != 0)? " callout_info" : "", ((controls & CTL_CALLOUT_NONE) != 0)? " callout_none" : "", ((controls & CTL_DFA) != 0)? " dfa" : "", + ((controls & CTL_EXPAND) != 0)? " expand" : "", ((controls & CTL_FINDLIMITS) != 0)? " find_limits" : "", ((controls & CTL_FULLBINCODE) != 0)? " fullbincode" : "", ((controls & CTL_GETALL) != 0)? " getall" : "", @@ -3435,9 +3589,15 @@ fprintf(outfile, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", ((controls & CTL_JITVERIFY) != 0)? " jitverify" : "", ((controls & CTL_MARK) != 0)? " mark" : "", ((controls & CTL_MEMORY) != 0)? " memory" : "", + ((controls & CTL_NL_SET) != 0)? " newline" : "", + ((controls & CTL_NULLCONTEXT) != 0)? " null_context" : "", ((controls & CTL_POSIX) != 0)? " posix" : "", ((controls & CTL_PUSH) != 0)? " push" : "", ((controls & CTL_STARTCHAR) != 0)? " startchar" : "", + ((controls2 & CTL2_SUBSTITUTE_EXTENDED) != 0)? " substitute_extended" : "", + ((controls2 & CTL2_SUBSTITUTE_OVERFLOW_LENGTH) != 0)? " substitute_overflow_length" : "", + ((controls2 & CTL2_SUBSTITUTE_UNKNOWN_UNSET) != 0)? " substitute_unknown_unset" : "", + ((controls2 & CTL2_SUBSTITUTE_UNSET_EMPTY) != 0)? " substitute_unset_empty" : "", ((controls & CTL_ZERO_TERMINATE) != 0)? " zero_terminate" : ""); } @@ -3461,10 +3621,11 @@ static void show_compile_options(uint32_t options, const char *before, const char *after) { if (options == 0) fprintf(outfile, "%s %s", before, after); -else fprintf(outfile, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", +else fprintf(outfile, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", before, ((options & PCRE2_ALT_BSUX) != 0)? " alt_bsux" : "", ((options & PCRE2_ALT_CIRCUMFLEX) != 0)? " alt_circumflex" : "", + ((options & PCRE2_ALT_VERBNAMES) != 0)? " alt_verbnames" : "", ((options & PCRE2_ALLOW_EMPTY_CLASS) != 0)? " allow_empty_class" : "", ((options & PCRE2_ANCHORED) != 0)? " anchored" : "", ((options & PCRE2_AUTO_CALLOUT) != 0)? " auto_callout" : "", @@ -3486,6 +3647,7 @@ else fprintf(outfile, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", ((options & PCRE2_NO_START_OPTIMIZE) != 0)? " no_start_optimize" : "", ((options & PCRE2_UCP) != 0)? " ucp" : "", ((options & PCRE2_UNGREEDY) != 0)? " ungreedy" : "", + ((options & PCRE2_USE_OFFSET_LIMIT) != 0)? " use_offset_limit" : "", ((options & PCRE2_UTF) != 0)? " utf" : "", after); } @@ -3633,8 +3795,9 @@ if ((pat_patctl.control & CTL_INFO) != 0) const uint8_t *start_bits; BOOL match_limit_set, recursion_limit_set; uint32_t backrefmax, bsr_convention, capture_count, first_ctype, first_cunit, - hascrorlf, jchanged, last_ctype, last_cunit, match_empty, match_limit, - minlength, nameentrysize, namecount, newline_convention, recursion_limit; + hasbackslashc, hascrorlf, jchanged, last_ctype, last_cunit, match_empty, + match_limit, minlength, nameentrysize, namecount, newline_convention, + recursion_limit; /* These info requests may return PCRE2_ERROR_UNSET. */ @@ -3674,6 +3837,7 @@ if ((pat_patctl.control & CTL_INFO) != 0) pattern_info(PCRE2_INFO_FIRSTBITMAP, &start_bits, FALSE) + pattern_info(PCRE2_INFO_FIRSTCODEUNIT, &first_cunit, FALSE) + pattern_info(PCRE2_INFO_FIRSTCODETYPE, &first_ctype, FALSE) + + pattern_info(PCRE2_INFO_HASBACKSLASHC, &hasbackslashc, FALSE) + pattern_info(PCRE2_INFO_HASCRORLF, &hascrorlf, FALSE) + pattern_info(PCRE2_INFO_JCHANGED, &jchanged, FALSE) + pattern_info(PCRE2_INFO_LASTCODEUNIT, &last_cunit, FALSE) + @@ -3704,7 +3868,7 @@ if ((pat_patctl.control & CTL_INFO) != 0) if (namecount > 0) { fprintf(outfile, "Named capturing subpatterns:\n"); - while (namecount-- > 0) + for (; namecount > 0; namecount--) { int imm2_size = test_mode == PCRE8_MODE ? 2 : 1; uint32_t length = (uint32_t)STRLEN(nametable + imm2_size); @@ -3728,8 +3892,9 @@ if ((pat_patctl.control & CTL_INFO) != 0) } } - if (hascrorlf) fprintf(outfile, "Contains explicit CR or LF match\n"); - if (match_empty) fprintf(outfile, "May match empty string\n"); + if (hascrorlf) fprintf(outfile, "Contains explicit CR or LF match\n"); + if (hasbackslashc) fprintf(outfile, "Contains \\C\n"); + if (match_empty) fprintf(outfile, "May match empty string\n"); pattern_info(PCRE2_INFO_ARGOPTIONS, &compile_options, FALSE); pattern_info(PCRE2_INFO_ALLOPTIONS, &overall_options, FALSE); @@ -3767,8 +3932,7 @@ if ((pat_patctl.control & CTL_INFO) != 0) fprintf(outfile, "\\R matches %s\n", (bsr_convention == PCRE2_BSR_UNICODE)? "any Unicode newline" : "CR, LF, or CRLF"); - if ((pat_patctl.control & CTL_NL_SET) != 0 || - (FLD(compiled_code, flags) & PCRE2_NL_SET) != 0) + if ((FLD(compiled_code, flags) & PCRE2_NL_SET) != 0) { switch (newline_convention) { @@ -3866,11 +4030,22 @@ if ((pat_patctl.control & CTL_INFO) != 0) if (FLD(compiled_code, executable_jit) != NULL) fprintf(outfile, "JIT compilation was successful\n"); else + { #ifdef SUPPORT_JIT - fprintf(outfile, "JIT compilation was not successful\n"); + int len; + fprintf(outfile, "JIT compilation was not successful"); + if (jitrc != 0) + { + fprintf(outfile, " ("); + PCRE2_GET_ERROR_MESSAGE(len, jitrc, pbuffer); + PCHARSV(CASTVAR(void *, pbuffer), 0, len, FALSE, outfile); + fprintf(outfile, ")"); + } + fprintf(outfile, "\n"); #else fprintf(outfile, "JIT support is not available in this version of PCRE2\n"); #endif + } } } @@ -3985,6 +4160,7 @@ FILE *f; PCRE2_SIZE serial_size; size_t i; int rc, cmd, cmdlen; +uint16_t first_listed_newline; const char *cmdname; uint8_t *argptr, *serial; @@ -4039,6 +4215,31 @@ switch(cmd) (void)decode_modifiers(argptr, CTX_DEFDAT, NULL, &def_datctl); break; + /* Check the default newline, and if not one of those listed, set up the + first one to be forced. An empty list unsets. */ + + case CMD_NEWLINE_DEFAULT: + local_newline_default = 0; /* Unset */ + first_listed_newline = 0; + for (;;) + { + while (isspace(*argptr)) argptr++; + if (*argptr == 0) break; + for (i = 1; i < sizeof(newlines)/sizeof(char *); i++) + { + size_t nlen = strlen(newlines[i]); + if (strncmpic(argptr, (const uint8_t *)newlines[i], nlen) == 0 && + isspace(argptr[nlen])) + { + if (i == NEWLINE_DEFAULT) return PR_OK; /* Default is valid */ + if (first_listed_newline == 0) first_listed_newline = i; + } + } + while (*argptr != 0 && !isspace(*argptr)) argptr++; + } + local_newline_default = first_listed_newline; + break; + /* Pop a compiled pattern off the stack. Modifiers that do not affect the compiled pattern (e.g. to give information) are permitted. The default pattern modifiers are ignored. */ @@ -4055,7 +4256,7 @@ switch(cmd) SET(compiled_code, patstack[--patstacknext]); if (pat_patctl.jit != 0) { - PCRE2_JIT_COMPILE(compiled_code, pat_patctl.jit); + PCRE2_JIT_COMPILE(jitrc, compiled_code, pat_patctl.jit); } if ((pat_patctl.control & CTL_MEMORY) != 0) show_memory_info(); if ((pat_patctl.control & CTL_ANYINFO) != 0) @@ -4178,10 +4379,12 @@ static int process_pattern(void) { BOOL utf; +uint32_t k; uint8_t *p = buffer; const uint8_t *use_tables; unsigned int delimiter = *p++; int errorcode; +void *use_pat_context; PCRE2_SIZE patlen; PCRE2_SIZE erroroffset; @@ -4226,6 +4429,20 @@ patlen = p - buffer - 2; if (!decode_modifiers(p, CTX_PAT, &pat_patctl, NULL)) return PR_SKIP; utf = (pat_patctl.options & PCRE2_UTF) != 0; +/* Check for mutually exclusive modifiers. At present, these are all in the +first control word. */ + +for (k = 0; k < sizeof(exclusive_pat_controls)/sizeof(uint32_t); k++) + { + uint32_t c = pat_patctl.control & exclusive_pat_controls[k]; + if (c != 0 && c != (c & (~c+1))) + { + show_controls(c, 0, "** Not allowed together:"); + fprintf(outfile, "\n"); + return PR_SKIP; + } + } + /* Assume full JIT compile for jitverify and/or jitfast if nothing else was specified. */ @@ -4233,28 +4450,16 @@ if (pat_patctl.jit == 0 && (pat_patctl.control & (CTL_JITVERIFY|CTL_JITFAST)) != 0) pat_patctl.jit = 7; -/* POSIX and 'push' do not play together. */ - -if ((pat_patctl.control & (CTL_POSIX|CTL_PUSH)) == (CTL_POSIX|CTL_PUSH)) - { - fprintf(outfile, "** The POSIX interface is incompatible with 'push'\n"); - return PR_ABEND; - } - /* Now copy the pattern to pbuffer8 for use in 8-bit testing and for reflecting -in callouts. Convert to binary if required. */ +in callouts. Convert from hex if required; this must necessarily be fewer +characters so will always fit in pbuffer8. Alternatively, process for +repetition if requested. */ if ((pat_patctl.control & CTL_HEXPAT) != 0) { uint8_t *pp, *pt; uint32_t c, d; - if ((pat_patctl.control & CTL_POSIX) != 0) - { - fprintf(outfile, "** Hex patterns are not supported for the POSIX API\n"); - return PR_SKIP; - } - pt = pbuffer8; for (pp = buffer + 1; *pp != 0; pp++) { @@ -4277,6 +4482,80 @@ if ((pat_patctl.control & CTL_HEXPAT) != 0) *pt = 0; patlen = pt - pbuffer8; } + +else if ((pat_patctl.control & CTL_EXPAND) != 0) + { + uint8_t *pp, *pt; + + pt = pbuffer8; + for (pp = buffer + 1; *pp != 0; pp++) + { + uint8_t *pc = pp; + uint32_t count = 1; + size_t length = 1; + + /* Check for replication syntax; if not found, the defaults just set will + prevail and one character will be copied. */ + + if (pp[0] == '\\' && pp[1] == '[') + { + uint8_t *pe; + for (pe = pp + 2; *pe != 0; pe++) + { + if (pe[0] == ']' && pe[1] == '{') + { + uint32_t clen = pe - pc - 2; + uint32_t i = 0; + pe += 2; + while (isdigit(*pe)) i = i * 10 + *pe++ - '0'; + if (*pe == '}') + { + if (i == 0) + { + fprintf(outfile, "** Zero repeat not allowed\n"); + return PR_SKIP; + } + pc += 2; + count = i; + length = clen; + pp = pe; + break; + } + } + } + } + + /* Add to output. If the buffer is too small expand it. The function for + expanding buffers always keeps buffer and pbuffer8 in step as far as their + size goes. */ + + while (pt + count * length > pbuffer8 + pbuffer8_size) + { + size_t pc_offset = pc - buffer; + size_t pp_offset = pp - buffer; + size_t pt_offset = pt - pbuffer8; + expand_input_buffers(); + pc = buffer + pc_offset; + pp = buffer + pp_offset; + pt = pbuffer8 + pt_offset; + } + + while (count-- > 0) + { + memcpy(pt, pc, length); + pt += length; + } + } + + *pt = 0; + patlen = pt - pbuffer8; + + if ((pat_patctl.control & CTL_INFO) != 0) + fprintf(outfile, "Expanded: %s\n", pbuffer8); + } + +/* Neither hex nor expanded, just copy the input verbatim. */ + else { strncpy((char *)pbuffer8, (char *)(buffer+1), patlen + 1); @@ -4358,12 +4637,16 @@ if ((pat_patctl.control & CTL_POSIX) != 0) pat_patctl.options & ~POSIX_SUPPORTED_COMPILE_OPTIONS, msg, ""); msg = ""; } - if ((pat_patctl.control & ~POSIX_SUPPORTED_COMPILE_CONTROLS) != 0) + if ((pat_patctl.control & ~POSIX_SUPPORTED_COMPILE_CONTROLS) != 0 || + (pat_patctl.control2 & ~POSIX_SUPPORTED_COMPILE_CONTROLS2) != 0) { - show_controls(pat_patctl.control & ~POSIX_SUPPORTED_COMPILE_CONTROLS, msg); + show_controls(pat_patctl.control & ~POSIX_SUPPORTED_COMPILE_CONTROLS, + pat_patctl.control2 & ~POSIX_SUPPORTED_COMPILE_CONTROLS2, msg); msg = ""; } + if (local_newline_default != 0) prmsg(&msg, "#newline_default"); + if (msg[0] == 0) fprintf(outfile, "\n"); /* Translate PCRE2 options to POSIX options and then compile. On success, set @@ -4380,8 +4663,24 @@ if ((pat_patctl.control & CTL_POSIX) != 0) rc = regcomp(&preg, (char *)pbuffer8, cflags); if (rc != 0) /* Failure */ { - (void)regerror(rc, &preg, (char *)pbuffer8, pbuffer8_size); + size_t bsize, usize; + + preg.re_pcre2_code = NULL; /* In case something was left in there */ + preg.re_match_data = NULL; + + bsize = (pat_patctl.regerror_buffsize != 0)? + pat_patctl.regerror_buffsize : pbuffer8_size; + if (bsize + 8 < pbuffer8_size) + memcpy(pbuffer8 + bsize, "DEADBEEF", 8); + usize = regerror(rc, &preg, (char *)pbuffer8, bsize); + fprintf(outfile, "Failed: POSIX code %d: %s\n", rc, pbuffer8); + if (usize > bsize) + { + fprintf(outfile, "** regerror() message truncated\n"); + if (memcmp(pbuffer8 + bsize, "DEADBEEF", 8) != 0) + fprintf(outfile, "** regerror() buffer overflow\n"); + } return PR_SKIP; } return PR_OK; @@ -4398,15 +4697,19 @@ if ((pat_patctl.control & CTL_PUSH) != 0) fprintf(outfile, "** Replacement text is not supported with 'push'.\n"); return PR_OK; } - if ((pat_patctl.control & ~PUSH_SUPPORTED_COMPILE_CONTROLS) != 0) + if ((pat_patctl.control & ~PUSH_SUPPORTED_COMPILE_CONTROLS) != 0 || + (pat_patctl.control2 & ~PUSH_SUPPORTED_COMPILE_CONTROLS2) != 0) { show_controls(pat_patctl.control & ~PUSH_SUPPORTED_COMPILE_CONTROLS, + pat_patctl.control2 & ~PUSH_SUPPORTED_COMPILE_CONTROLS2, "** Ignored when compiled pattern is stacked with 'push':"); fprintf(outfile, "\n"); } - if ((pat_patctl.control & PUSH_COMPILE_ONLY_CONTROLS) != 0) + if ((pat_patctl.control & PUSH_COMPILE_ONLY_CONTROLS) != 0 || + (pat_patctl.control2 & PUSH_COMPILE_ONLY_CONTROLS2) != 0) { show_controls(pat_patctl.control & PUSH_COMPILE_ONLY_CONTROLS, + pat_patctl.control2 & PUSH_COMPILE_ONLY_CONTROLS2, "** Applies only to compile when pattern is stacked with 'push':"); fprintf(outfile, "\n"); } @@ -4453,6 +4756,21 @@ if we had a hex pattern. */ if ((pat_patctl.control & CTL_HEXPAT) == 0) patlen = PCRE2_ZERO_TERMINATED; +/* If #newline_default has been used and the library was not compiled with an +appropriate default newline setting, local_newline_default will be non-zero. We +use this if there is no explicit newline modifier. */ + +if ((pat_patctl.control & CTL_NL_SET) == 0 && local_newline_default != 0) + { + SETFLD(pat_context, newline_convention, local_newline_default); + } + +/* The nullcontext modifier is used to test calling pcre2_compile() with a NULL +context. */ + +use_pat_context = ((pat_patctl.control & CTL_NULLCONTEXT) != 0)? + NULL : PTR(pat_context); + /* Compile many times when timing. */ if (timeit > 0) @@ -4463,7 +4781,7 @@ if (timeit > 0) { clock_t start_time = clock(); PCRE2_COMPILE(compiled_code, pbuffer, patlen, - pat_patctl.options|forbid_utf, &errorcode, &erroroffset, pat_context); + pat_patctl.options|forbid_utf, &errorcode, &erroroffset, use_pat_context); time_taken += clock() - start_time; if (TEST(compiled_code, !=, NULL)) { SUB1(pcre2_code_free, compiled_code); } @@ -4477,7 +4795,7 @@ if (timeit > 0) /* A final compile that is used "for real". */ PCRE2_COMPILE(compiled_code, pbuffer, patlen, pat_patctl.options|forbid_utf, - &errorcode, &erroroffset, pat_context); + &errorcode, &erroroffset, use_pat_context); /* Compilation failed; go back for another re, skipping to blank line if non-interactive. */ @@ -4527,9 +4845,10 @@ if (pat_patctl.jit != 0) clock_t start_time; SUB1(pcre2_code_free, compiled_code); PCRE2_COMPILE(compiled_code, pbuffer, patlen, - pat_patctl.options|forbid_utf, &errorcode, &erroroffset, pat_context); + pat_patctl.options|forbid_utf, &errorcode, &erroroffset, + use_pat_context); start_time = clock(); - PCRE2_JIT_COMPILE(compiled_code, pat_patctl.jit); + PCRE2_JIT_COMPILE(jitrc,compiled_code, pat_patctl.jit); time_taken += clock() - start_time; } total_jit_compile_time += time_taken; @@ -4539,10 +4858,18 @@ if (pat_patctl.jit != 0) } else { - PCRE2_JIT_COMPILE(compiled_code, pat_patctl.jit); + PCRE2_JIT_COMPILE(jitrc, compiled_code, pat_patctl.jit); } } +/* If an explicit newline modifier was given, set the information flag in the +pattern so that it is preserved over push/pop. */ + +if ((pat_patctl.control & CTL_NL_SET) != 0) + { + SETFLD(compiled_code, flags, FLD(compiled_code, flags) | PCRE2_NL_SET); + } + /* Output code size and other information if requested. */ if ((pat_patctl.control & CTL_MEMORY) != 0) show_memory_info(); @@ -4599,10 +4926,10 @@ for (;;) if ((pat_patctl.control & CTL_JITFAST) != 0) PCRE2_JIT_MATCH(capcount, compiled_code, pp, ulen, dat_datctl.offset, - dat_datctl.options, match_data, dat_context); + dat_datctl.options, match_data, PTR(dat_context)); else PCRE2_MATCH(capcount, compiled_code, pp, ulen, dat_datctl.offset, - dat_datctl.options, match_data, dat_context); + dat_datctl.options, match_data, PTR(dat_context)); if (capcount == errnumber) { @@ -5025,10 +5352,11 @@ process_data(void) { PCRE2_SIZE len, ulen; uint32_t gmatched; -uint32_t c; +uint32_t c, k; uint32_t g_notempty = 0; uint8_t *p, *pp, *start_rep; size_t needlen; +void *use_dat_context; BOOL utf; #ifdef SUPPORT_PCRE2_8 @@ -5050,6 +5378,7 @@ matching. */ DATCTXCPY(dat_context, default_dat_context); memcpy(&dat_datctl, &def_datctl, sizeof(datctl)); dat_datctl.control |= (pat_patctl.control & CTL_ALLPD); +dat_datctl.control2 |= (pat_patctl.control2 & CTL2_ALLPD); strcpy((char *)dat_datctl.replacement, (char *)pat_patctl.replacement); /* Initialize for scanning the data line. */ @@ -5098,7 +5427,7 @@ if (dbuffer != NULL) the number of code units that will be needed (though the buffer may have to be extended if replication is involved). */ -needlen = (size_t)(len * code_unit_size); +needlen = (size_t)((len+1) * code_unit_size); if (dbuffer == NULL || needlen >= dbuffer_size) { while (needlen >= dbuffer_size) dbuffer_size *= 2; @@ -5367,38 +5696,44 @@ ulen = len/code_unit_size; /* Length in code units */ if (p[-1] != 0 && !decode_modifiers(p, CTX_DAT, NULL, &dat_datctl)) return PR_OK; -/* Check for mutually exclusive modifiers. */ +/* Check for mutually exclusive modifiers. At present, these are all in the +first control word. */ -c = dat_datctl.control & EXCLUSIVE_DAT_CONTROLS; -if (c - (c & -c) != 0) +for (k = 0; k < sizeof(exclusive_dat_controls)/sizeof(uint32_t); k++) { - show_controls(c, "** Not allowed together:"); - fprintf(outfile, "\n"); + c = dat_datctl.control & exclusive_dat_controls[k]; + if (c != 0 && c != (c & (~c+1))) + { + show_controls(c, 0, "** Not allowed together:"); + fprintf(outfile, "\n"); + return PR_OK; + } + } + +if (pat_patctl.replacement[0] != 0 && + (dat_datctl.control & CTL_NULLCONTEXT) != 0) + { + fprintf(outfile, "** Replacement text is not supported with null_context.\n"); return PR_OK; } -/* If we have explicit valgrind support, mark the data from after its end to -the end of the buffer as unaddressable, so that a read over the end of the -buffer will be seen by valgrind, even if it doesn't cause a crash. If we're not -building with valgrind support, at least move the data to the end of the buffer -so that it might at least cause a crash. If we are using the POSIX interface, -or testing zero-termination, we must include the terminating zero. */ +/* We now have the subject in dbuffer, with len containing the byte length, and +ulen containing the code unit length. Move the data to the end of the buffer so +that a read over the end can be caught by valgrind or other means. If we have +explicit valgrind support, mark the unused start of the buffer unaddressable. +If we are using the POSIX interface, or testing zero-termination, we must +include the terminating zero in the usable data. */ -pp = dbuffer; c = code_unit_size * (((pat_patctl.control & CTL_POSIX) + (dat_datctl.control & CTL_ZERO_TERMINATE) != 0)? 1:0); - +pp = memmove(dbuffer + dbuffer_size - len - c, dbuffer, len + c); #ifdef SUPPORT_VALGRIND - VALGRIND_MAKE_MEM_NOACCESS(dbuffer + len + c, dbuffer_size - (len + c)); -#else - pp = memmove(pp + dbuffer_size - len - c, pp, len + c); + VALGRIND_MAKE_MEM_NOACCESS(dbuffer, dbuffer_size - (len + c)); #endif -/* We now have len containing the byte length, ulen containing the code unit -length, and pp pointing to the subject string. POSIX matching is only possible -in 8-bit mode, and it does not support timing or other fancy features. Some -were checked at compile time, but we need to check the match-time settings -here. */ +/* Now pp points to the subject string. POSIX matching is only possible in +8-bit mode, and it does not support timing or other fancy features. Some were +checked at compile time, but we need to check the match-time settings here. */ #ifdef SUPPORT_PCRE2_8 if ((pat_patctl.control & CTL_POSIX) != 0) @@ -5422,9 +5757,11 @@ if ((pat_patctl.control & CTL_POSIX) != 0) show_match_options(dat_datctl.options & ~POSIX_SUPPORTED_MATCH_OPTIONS); msg = ""; } - if ((dat_datctl.control & ~POSIX_SUPPORTED_MATCH_CONTROLS) != 0) + if ((dat_datctl.control & ~POSIX_SUPPORTED_MATCH_CONTROLS) != 0 || + (dat_datctl.control2 & ~POSIX_SUPPORTED_MATCH_CONTROLS2) != 0) { - show_controls(dat_datctl.control & ~POSIX_SUPPORTED_MATCH_CONTROLS, msg); + show_controls(dat_datctl.control & ~POSIX_SUPPORTED_MATCH_CONTROLS, + dat_datctl.control2 & ~POSIX_SUPPORTED_MATCH_CONTROLS2, msg); msg = ""; } @@ -5455,14 +5792,14 @@ if ((pat_patctl.control & CTL_POSIX) != 0) if (pmatch[i].rm_so >= 0) { fprintf(outfile, "%2d: ", (int)i); - PCHARSV(dbuffer, pmatch[i].rm_so, + PCHARSV(pp, pmatch[i].rm_so, pmatch[i].rm_eo - pmatch[i].rm_so, utf, outfile); fprintf(outfile, "\n"); if ((i == 0 && (dat_datctl.control & CTL_AFTERTEXT) != 0) || (dat_datctl.control & CTL_ALLAFTERTEXT) != 0) { fprintf(outfile, "%2d+ ", (int)i); - PCHARSV(dbuffer, pmatch[i].rm_eo, len - pmatch[i].rm_eo, + PCHARSV(pp, pmatch[i].rm_eo, len - pmatch[i].rm_eo, utf, outfile); fprintf(outfile, "\n"); } @@ -5498,6 +5835,12 @@ if ((dat_datctl.control & (CTL_ALLUSEDTEXT|CTL_DFA)) == CTL_ALLUSEDTEXT && if ((dat_datctl.control & CTL_ZERO_TERMINATE) != 0) ulen = PCRE2_ZERO_TERMINATED; +/* The nullcontext modifier is used to test calling pcre2_[jit_]match() with a +NULL context. */ + +use_dat_context = ((dat_datctl.control & CTL_NULLCONTEXT) != 0)? + NULL : PTR(dat_context); + /* Enable display of malloc/free if wanted. */ show_memory = (dat_datctl.control & CTL_MEMORY) != 0; @@ -5571,7 +5914,7 @@ if (dat_datctl.replacement[0] != 0) uint8_t *pr; uint8_t rbuffer[REPLACE_BUFFSIZE]; uint8_t nbuffer[REPLACE_BUFFSIZE]; - uint32_t goption; + uint32_t xoptions; PCRE2_SIZE rlen, nsize, erroroffset; BOOL badutf = FALSE; @@ -5588,8 +5931,17 @@ if (dat_datctl.replacement[0] != 0) if (timeitm) fprintf(outfile, "** Timing is not supported with replace: ignored\n"); - goption = ((dat_datctl.control & CTL_GLOBAL) == 0)? 0 : - PCRE2_SUBSTITUTE_GLOBAL; + xoptions = (((dat_datctl.control & CTL_GLOBAL) == 0)? 0 : + PCRE2_SUBSTITUTE_GLOBAL) | + (((dat_datctl.control2 & CTL2_SUBSTITUTE_EXTENDED) == 0)? 0 : + PCRE2_SUBSTITUTE_EXTENDED) | + (((dat_datctl.control2 & CTL2_SUBSTITUTE_OVERFLOW_LENGTH) == 0)? 0 : + PCRE2_SUBSTITUTE_OVERFLOW_LENGTH) | + (((dat_datctl.control2 & CTL2_SUBSTITUTE_UNKNOWN_UNSET) == 0)? 0 : + PCRE2_SUBSTITUTE_UNKNOWN_UNSET) | + (((dat_datctl.control2 & CTL2_SUBSTITUTE_UNSET_EMPTY) == 0)? 0 : + PCRE2_SUBSTITUTE_UNSET_EMPTY); + SETCASTPTR(r, rbuffer); /* Sets r8, r16, or r32, as appropriate. */ pr = dat_datctl.replacement; @@ -5676,14 +6028,21 @@ if (dat_datctl.replacement[0] != 0) else rlen = (CASTVAR(uint8_t *, r) - rbuffer)/code_unit_size; PCRE2_SUBSTITUTE(rc, compiled_code, pp, ulen, dat_datctl.offset, - dat_datctl.options|goption, match_data, dat_context, + dat_datctl.options|xoptions, match_data, dat_context, rbuffer, rlen, nbuffer, &nsize); if (rc < 0) { - fprintf(outfile, "Failed: error %d: ", rc); - PCRE2_GET_ERROR_MESSAGE(nsize, rc, pbuffer); - PCHARSV(CASTVAR(void *, pbuffer), 0, nsize, FALSE, outfile); + PCRE2_SIZE msize; + fprintf(outfile, "Failed: error %d", rc); + if (rc != PCRE2_ERROR_NOMEMORY && nsize != PCRE2_UNSET) + fprintf(outfile, " at offset %ld in replacement", (long int)nsize); + fprintf(outfile, ": "); + PCRE2_GET_ERROR_MESSAGE(msize, rc, pbuffer); + PCHARSV(CASTVAR(void *, pbuffer), 0, msize, FALSE, outfile); + if (rc == PCRE2_ERROR_NOMEMORY && + (xoptions & PCRE2_SUBSTITUTE_OVERFLOW_LENGTH) != 0) + fprintf(outfile, ": %ld code units are needed", (long int)nsize); } else { @@ -5752,7 +6111,7 @@ else for (gmatched = 0;; gmatched++) { PCRE2_DFA_MATCH(capcount, compiled_code, pp, ulen, dat_datctl.offset, dat_datctl.options | g_notempty, match_data, - dat_context, dfa_workspace, DFA_WS_DIMENSION); + use_dat_context, dfa_workspace, DFA_WS_DIMENSION); } } @@ -5763,7 +6122,7 @@ else for (gmatched = 0;; gmatched++) { PCRE2_JIT_MATCH(capcount, compiled_code, pp, ulen, dat_datctl.offset, dat_datctl.options | g_notempty, match_data, - dat_context); + use_dat_context); } } @@ -5774,7 +6133,7 @@ else for (gmatched = 0;; gmatched++) { PCRE2_MATCH(capcount, compiled_code, pp, ulen, dat_datctl.offset, dat_datctl.options | g_notempty, match_data, - dat_context); + use_dat_context); } } total_match_time += (time_taken = clock() - start_time); @@ -5822,7 +6181,7 @@ else for (gmatched = 0;; gmatched++) dfa_workspace[0] = -1; /* To catch bad restart */ PCRE2_DFA_MATCH(capcount, compiled_code, pp, ulen, dat_datctl.offset, dat_datctl.options | g_notempty, match_data, - dat_context, dfa_workspace, DFA_WS_DIMENSION); + use_dat_context, dfa_workspace, DFA_WS_DIMENSION); if (capcount == 0) { fprintf(outfile, "Matched, but offsets vector is too small to show all matches\n"); @@ -5833,10 +6192,10 @@ else for (gmatched = 0;; gmatched++) { if ((pat_patctl.control & CTL_JITFAST) != 0) PCRE2_JIT_MATCH(capcount, compiled_code, pp, ulen, dat_datctl.offset, - dat_datctl.options | g_notempty, match_data, dat_context); + dat_datctl.options | g_notempty, match_data, use_dat_context); else PCRE2_MATCH(capcount, compiled_code, pp, ulen, dat_datctl.offset, - dat_datctl.options | g_notempty, match_data, dat_context); + dat_datctl.options | g_notempty, match_data, use_dat_context); if (capcount == 0) { fprintf(outfile, "Matched, but too many substrings\n"); @@ -6046,7 +6405,8 @@ else for (gmatched = 0;; gmatched++) TESTFLD(match_data, mark, !=, NULL)) { fprintf(outfile, ", mark="); - PCHARS(rubriclength, CASTFLD(void *, match_data, mark), 0, -1, utf, outfile); + PCHARS(rubriclength, CASTFLD(void *, match_data, mark), 0, -1, utf, + outfile); rubriclength += 7; } fprintf(outfile, ": "); @@ -6344,6 +6704,7 @@ printf(" -b set default pattern control 'fullbincode'\n"); printf(" -C show PCRE2 compile-time options and exit\n"); printf(" -C arg show a specific compile-time option and exit with its\n"); printf(" value if numeric (else 0). The arg can be:\n"); +printf(" backslash-C use of \\C is enabled [0, 1]\n"); printf(" bsr \\R type [ANYCRLF, ANY]\n"); printf(" ebcdic compiled for EBCDIC character code [0,1]\n"); printf(" ebcdic-nl NL code if compiled for EBCDIC\n"); @@ -6454,6 +6815,9 @@ printf("Compiled with\n"); #ifdef EBCDIC printf(" EBCDIC code support: LF is 0x%02x\n", CHAR_LF); +#if defined NATIVE_ZOS +printf(" EBCDIC code page %s or similar\n", pcrz_cpversion()); +#endif #endif #ifdef SUPPORT_PCRE2_8 @@ -6492,6 +6856,11 @@ print_newline_config(optval, FALSE); (void)PCRE2_CONFIG(PCRE2_CONFIG_BSR, &optval); printf(" \\R matches %s\n", optval? "CR, LF, or CRLF only" : "all Unicode newlines"); +#ifdef NEVER_BACKSLASH_C +printf(" \\C is not supported\n"); +#else +printf(" \\C is supported\n"); +#endif (void)PCRE2_CONFIG(PCRE2_CONFIG_LINKSIZE, &optval); printf(" Internal link size = %d\n", optval); (void)PCRE2_CONFIG(PCRE2_CONFIG_PARENSLIMIT, &optval); @@ -6532,7 +6901,8 @@ control blocks must be the same so that common options and controls such as We cannot test this till runtime because "offsetof" does not work in the preprocessor. */ -if (PO(options) != DO(options) || PO(control) != DO(control)) +if (PO(options) != DO(options) || PO(control) != DO(control) || + PO(control2) != DO(control2)) { fprintf(stderr, "** Coding error: " "options and control offsets for pattern and data must be the same.\n"); @@ -6589,8 +6959,9 @@ def_datctl.cfail[0] = def_datctl.cfail[1] = CFAIL_UNSET; while (argc > 1 && argv[op][0] == '-' && argv[op][1] != 0) { - const char *endptr; + char *endptr; char *arg = argv[op]; + unsigned long uli; /* Display and/or set return code for configuration options. */ @@ -6640,7 +7011,7 @@ while (argc > 1 && argv[op][0] == '-' && argv[op][1] != 0) /* Set system stack size */ else if (strcmp(arg, "-S") == 0 && argc > 2 && - ((stack_size = get_value(argv[op+1], &endptr)), *endptr == 0)) + ((uli = strtoul(argv[op+1], &endptr, 10)), *endptr == 0)) { #if defined(_WIN32) || defined(WIN32) || defined(__minix) || defined(NATIVE_ZOS) || defined(__VMS) fprintf(stderr, "pcre2test: -S is not supported on this OS\n"); @@ -6648,6 +7019,12 @@ while (argc > 1 && argv[op][0] == '-' && argv[op][1] != 0) #else int rc; struct rlimit rlim; + if (U32OVERFLOW(uli)) + { + fprintf(stderr, "+++ Argument for -S is too big\n"); + exit(1); + } + stack_size = (uint32_t)uli; getrlimit(RLIMIT_STACK, &rlim); rlim.rlim_cur = stack_size * 1024 * 1024; if (rlim.rlim_cur > rlim.rlim_max) @@ -6690,12 +7067,16 @@ while (argc > 1 && argv[op][0] == '-' && argv[op][1] != 0) else if (strcmp(arg, "-t") == 0 || strcmp(arg, "-tm") == 0 || strcmp(arg, "-T") == 0 || strcmp(arg, "-TM") == 0) { - int temp; int both = arg[2] == 0; showtotaltimes = arg[1] == 'T'; - if (argc > 2 && (temp = get_value(argv[op+1], &endptr), *endptr == 0)) + if (argc > 2 && (uli = strtoul(argv[op+1], &endptr, 10), *endptr == 0)) { - timeitm = temp; + if (U32OVERFLOW(uli)) + { + fprintf(stderr, "+++ Argument for %s is too big\n", arg); + exit(1); + } + timeitm = (int)uli; op++; argc--; } @@ -6915,7 +7296,8 @@ while (notdone) skipping = FALSE; setlocale(LC_CTYPE, "C"); } - else if (!skipping) rc = process_data(); + else if (!skipping && !(p[0] == '\\' && p[1] == '=' && isspace(p[2]))) + rc = process_data(); } /* We do not have a pattern set up for testing. Lines starting with # are diff --git a/pcre2-10.20/src/sljit/sljitConfig.h b/pcre2-10.21/src/sljit/sljitConfig.h similarity index 100% rename from pcre2-10.20/src/sljit/sljitConfig.h rename to pcre2-10.21/src/sljit/sljitConfig.h diff --git a/pcre2-10.20/src/sljit/sljitConfigInternal.h b/pcre2-10.21/src/sljit/sljitConfigInternal.h similarity index 99% rename from pcre2-10.20/src/sljit/sljitConfigInternal.h rename to pcre2-10.21/src/sljit/sljitConfigInternal.h index 8a4b9664f..16e3547c9 100644 --- a/pcre2-10.20/src/sljit/sljitConfigInternal.h +++ b/pcre2-10.21/src/sljit/sljitConfigInternal.h @@ -613,6 +613,12 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void); #define SLJIT_LOCALS_OFFSET_BASE ((23 + 1) * sizeof(sljit_sw)) #endif +#elif (defined SLJIT_CONFIG_TILEGX && SLJIT_CONFIG_TILEGX) + +#define SLJIT_NUMBER_OF_REGISTERS 10 +#define SLJIT_NUMBER_OF_SAVED_REGISTERS 5 +#define SLJIT_LOCALS_OFFSET_BASE 0 + #elif (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED) #define SLJIT_NUMBER_OF_REGISTERS 0 diff --git a/pcre2-10.20/src/sljit/sljitExecAllocator.c b/pcre2-10.21/src/sljit/sljitExecAllocator.c similarity index 100% rename from pcre2-10.20/src/sljit/sljitExecAllocator.c rename to pcre2-10.21/src/sljit/sljitExecAllocator.c diff --git a/pcre2-10.20/src/sljit/sljitLir.c b/pcre2-10.21/src/sljit/sljitLir.c similarity index 100% rename from pcre2-10.20/src/sljit/sljitLir.c rename to pcre2-10.21/src/sljit/sljitLir.c diff --git a/pcre2-10.20/src/sljit/sljitLir.h b/pcre2-10.21/src/sljit/sljitLir.h similarity index 97% rename from pcre2-10.20/src/sljit/sljitLir.h rename to pcre2-10.21/src/sljit/sljitLir.h index f0969dac2..2e2e9ac09 100644 --- a/pcre2-10.20/src/sljit/sljitLir.h +++ b/pcre2-10.21/src/sljit/sljitLir.h @@ -869,34 +869,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_op2(struct sljit_compiler *compiler sljit_si src1, sljit_sw src1w, sljit_si src2, sljit_sw src2w); -/* The following function is a helper function for sljit_emit_op_custom. - It returns with the real machine register index ( >=0 ) of any SLJIT_R, - SLJIT_S and SLJIT_SP registers. - - Note: it returns with -1 for virtual registers (only on x86-32). */ - -SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_get_register_index(sljit_si reg); - -/* The following function is a helper function for sljit_emit_op_custom. - It returns with the real machine register index of any SLJIT_FLOAT register. - - Note: the index is always an even number on ARM (except ARM-64), MIPS, and SPARC. */ - -SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_get_float_register_index(sljit_si reg); - -/* Any instruction can be inserted into the instruction stream by - sljit_emit_op_custom. It has a similar purpose as inline assembly. - The size parameter must match to the instruction size of the target - architecture: - - x86: 0 < size <= 15. The instruction argument can be byte aligned. - Thumb2: if size == 2, the instruction argument must be 2 byte aligned. - if size == 4, the instruction argument must be 4 byte aligned. - Otherwise: size must be 4 and instruction argument must be 4 byte aligned. */ - -SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_op_custom(struct sljit_compiler *compiler, - void *instruction, sljit_si size); - /* Returns with non-zero if fpu is available. */ SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_is_fpu_available(void); @@ -1214,4 +1186,64 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_set_function_context(void** func_ptr, struct #endif /* !(defined SLJIT_INDIRECT_CALL && SLJIT_INDIRECT_CALL) */ +/* --------------------------------------------------------------------- */ +/* CPU specific functions */ +/* --------------------------------------------------------------------- */ + +/* The following function is a helper function for sljit_emit_op_custom. + It returns with the real machine register index ( >=0 ) of any SLJIT_R, + SLJIT_S and SLJIT_SP registers. + + Note: it returns with -1 for virtual registers (only on x86-32). */ + +SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_get_register_index(sljit_si reg); + +/* The following function is a helper function for sljit_emit_op_custom. + It returns with the real machine register index of any SLJIT_FLOAT register. + + Note: the index is always an even number on ARM (except ARM-64), MIPS, and SPARC. */ + +SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_get_float_register_index(sljit_si reg); + +/* Any instruction can be inserted into the instruction stream by + sljit_emit_op_custom. It has a similar purpose as inline assembly. + The size parameter must match to the instruction size of the target + architecture: + + x86: 0 < size <= 15. The instruction argument can be byte aligned. + Thumb2: if size == 2, the instruction argument must be 2 byte aligned. + if size == 4, the instruction argument must be 4 byte aligned. + Otherwise: size must be 4 and instruction argument must be 4 byte aligned. */ + +SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_op_custom(struct sljit_compiler *compiler, + void *instruction, sljit_si size); + +#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) + +/* Returns with non-zero if sse2 is available. */ + +SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_x86_is_sse2_available(void); + +/* Returns with non-zero if cmov instruction is available. */ + +SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_x86_is_cmov_available(void); + +/* Emit a conditional mov instruction on x86 CPUs. This instruction + moves src to destination, if the condition is satisfied. Unlike + other arithmetic instructions, destination must be a register. + Before such instructions are emitted, cmov support should be + checked by sljit_x86_is_cmov_available function. + type must be between SLJIT_EQUAL and SLJIT_S_ORDERED + dst_reg must be a valid register and it can be combined + with SLJIT_INT_OP to perform 32 bit arithmetic + Flags: I - (never set any flags) + */ + +SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_x86_emit_cmov(struct sljit_compiler *compiler, + sljit_si type, + sljit_si dst_reg, + sljit_si src, sljit_sw srcw); + +#endif + #endif /* _SLJIT_LIR_H_ */ diff --git a/pcre2-10.20/src/sljit/sljitNativeARM_32.c b/pcre2-10.21/src/sljit/sljitNativeARM_32.c similarity index 100% rename from pcre2-10.20/src/sljit/sljitNativeARM_32.c rename to pcre2-10.21/src/sljit/sljitNativeARM_32.c diff --git a/pcre2-10.20/src/sljit/sljitNativeARM_64.c b/pcre2-10.21/src/sljit/sljitNativeARM_64.c similarity index 100% rename from pcre2-10.20/src/sljit/sljitNativeARM_64.c rename to pcre2-10.21/src/sljit/sljitNativeARM_64.c diff --git a/pcre2-10.20/src/sljit/sljitNativeARM_T2_32.c b/pcre2-10.21/src/sljit/sljitNativeARM_T2_32.c similarity index 100% rename from pcre2-10.20/src/sljit/sljitNativeARM_T2_32.c rename to pcre2-10.21/src/sljit/sljitNativeARM_T2_32.c diff --git a/pcre2-10.20/src/sljit/sljitNativeMIPS_32.c b/pcre2-10.21/src/sljit/sljitNativeMIPS_32.c similarity index 100% rename from pcre2-10.20/src/sljit/sljitNativeMIPS_32.c rename to pcre2-10.21/src/sljit/sljitNativeMIPS_32.c diff --git a/pcre2-10.20/src/sljit/sljitNativeMIPS_64.c b/pcre2-10.21/src/sljit/sljitNativeMIPS_64.c similarity index 100% rename from pcre2-10.20/src/sljit/sljitNativeMIPS_64.c rename to pcre2-10.21/src/sljit/sljitNativeMIPS_64.c diff --git a/pcre2-10.20/src/sljit/sljitNativeMIPS_common.c b/pcre2-10.21/src/sljit/sljitNativeMIPS_common.c similarity index 100% rename from pcre2-10.20/src/sljit/sljitNativeMIPS_common.c rename to pcre2-10.21/src/sljit/sljitNativeMIPS_common.c diff --git a/pcre2-10.20/src/sljit/sljitNativePPC_32.c b/pcre2-10.21/src/sljit/sljitNativePPC_32.c similarity index 100% rename from pcre2-10.20/src/sljit/sljitNativePPC_32.c rename to pcre2-10.21/src/sljit/sljitNativePPC_32.c diff --git a/pcre2-10.20/src/sljit/sljitNativePPC_64.c b/pcre2-10.21/src/sljit/sljitNativePPC_64.c similarity index 100% rename from pcre2-10.20/src/sljit/sljitNativePPC_64.c rename to pcre2-10.21/src/sljit/sljitNativePPC_64.c diff --git a/pcre2-10.20/src/sljit/sljitNativePPC_common.c b/pcre2-10.21/src/sljit/sljitNativePPC_common.c similarity index 100% rename from pcre2-10.20/src/sljit/sljitNativePPC_common.c rename to pcre2-10.21/src/sljit/sljitNativePPC_common.c diff --git a/pcre2-10.20/src/sljit/sljitNativeSPARC_32.c b/pcre2-10.21/src/sljit/sljitNativeSPARC_32.c similarity index 100% rename from pcre2-10.20/src/sljit/sljitNativeSPARC_32.c rename to pcre2-10.21/src/sljit/sljitNativeSPARC_32.c diff --git a/pcre2-10.20/src/sljit/sljitNativeSPARC_common.c b/pcre2-10.21/src/sljit/sljitNativeSPARC_common.c similarity index 100% rename from pcre2-10.20/src/sljit/sljitNativeSPARC_common.c rename to pcre2-10.21/src/sljit/sljitNativeSPARC_common.c diff --git a/pcre2-10.20/src/sljit/sljitNativeTILEGX-encoder.c b/pcre2-10.21/src/sljit/sljitNativeTILEGX-encoder.c similarity index 100% rename from pcre2-10.20/src/sljit/sljitNativeTILEGX-encoder.c rename to pcre2-10.21/src/sljit/sljitNativeTILEGX-encoder.c diff --git a/pcre2-10.20/src/sljit/sljitNativeTILEGX_64.c b/pcre2-10.21/src/sljit/sljitNativeTILEGX_64.c similarity index 92% rename from pcre2-10.20/src/sljit/sljitNativeTILEGX_64.c rename to pcre2-10.21/src/sljit/sljitNativeTILEGX_64.c index 1d6aa5a11..4d40392fa 100644 --- a/pcre2-10.20/src/sljit/sljitNativeTILEGX_64.c +++ b/pcre2-10.21/src/sljit/sljitNativeTILEGX_64.c @@ -35,21 +35,21 @@ #define SIMM_16BIT_MIN (-0x8000) #define SIMM_17BIT_MAX (0xffff) #define SIMM_17BIT_MIN (-0x10000) -#define SIMM_32BIT_MIN (-0x80000000) #define SIMM_32BIT_MAX (0x7fffffff) -#define SIMM_48BIT_MIN (0x800000000000L) +#define SIMM_32BIT_MIN (-0x7fffffff - 1) #define SIMM_48BIT_MAX (0x7fffffff0000L) +#define SIMM_48BIT_MIN (-0x800000000000L) #define IMM16(imm) ((imm) & 0xffff) #define UIMM_16BIT_MAX (0xffff) -#define TMP_REG1 (SLJIT_NO_REGISTERS + 1) -#define TMP_REG2 (SLJIT_NO_REGISTERS + 2) -#define TMP_REG3 (SLJIT_NO_REGISTERS + 3) -#define ADDR_TMP (SLJIT_NO_REGISTERS + 4) +#define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2) +#define TMP_REG2 (SLJIT_NUMBER_OF_REGISTERS + 3) +#define TMP_REG3 (SLJIT_NUMBER_OF_REGISTERS + 4) +#define ADDR_TMP (SLJIT_NUMBER_OF_REGISTERS + 5) #define PIC_ADDR_REG TMP_REG2 -static SLJIT_CONST sljit_ub reg_map[SLJIT_NO_REGISTERS + 5] = { +static SLJIT_CONST sljit_ub reg_map[SLJIT_NUMBER_OF_REGISTERS + 6] = { 63, 0, 1, 2, 3, 4, 30, 31, 32, 33, 34, 54, 5, 16, 6, 7 }; @@ -58,11 +58,6 @@ static SLJIT_CONST sljit_ub reg_map[SLJIT_NO_REGISTERS + 5] = { #define TMP_REG2_mapped 16 #define TMP_REG3_mapped 6 #define ADDR_TMP_mapped 7 -#define SLJIT_SAVED_REG1_mapped 30 -#define SLJIT_SAVED_REG2_mapped 31 -#define SLJIT_SAVED_REG3_mapped 32 -#define SLJIT_SAVED_EREG1_mapped 33 -#define SLJIT_SAVED_EREG2_mapped 34 /* Flags are keept in volatile registers. */ #define EQUAL_FLAG 8 @@ -399,6 +394,9 @@ static sljit_si push_inst(struct sljit_compiler *compiler, sljit_ins ins) #define SUB(dst, srca, srcb) \ push_3_buffer(compiler, TILEGX_OPC_SUB, dst, srca, srcb, __LINE__) +#define MUL(dst, srca, srcb) \ + push_3_buffer(compiler, TILEGX_OPC_MULX, dst, srca, srcb, __LINE__) + #define NOR(dst, srca, srcb) \ push_3_buffer(compiler, TILEGX_OPC_NOR, dst, srca, srcb, __LINE__) @@ -547,8 +545,8 @@ const struct Format* compute_format() const struct Format* match = NULL; const struct Format *b = NULL; - unsigned int i = 0; - for (i; i < sizeof formats / sizeof formats[0]; i++) { + unsigned int i; + for (i = 0; i < sizeof formats / sizeof formats[0]; i++) { b = &formats[i]; if ((b->pipe_mask & compatible_pipes) == b->pipe_mask) { match = b; @@ -625,7 +623,6 @@ tilegx_bundle_bits get_bundle_bit(struct jit_instr *inst) static sljit_si update_buffer(struct sljit_compiler *compiler) { - int count; int i; int orig_index = inst_buf_index; struct jit_instr inst0 = inst_buf[0]; @@ -738,8 +735,10 @@ static sljit_si update_buffer(struct sljit_compiler *compiler) static sljit_si flush_buffer(struct sljit_compiler *compiler) { - while (inst_buf_index != 0) - update_buffer(compiler); + while (inst_buf_index != 0) { + FAIL_IF(update_buffer(compiler)); + } + return SLJIT_SUCCESS; } static sljit_si push_4_buffer(struct sljit_compiler *compiler, tilegx_mnemonic opc, int op0, int op1, int op2, int op3, int line) @@ -787,6 +786,7 @@ static sljit_si push_3_buffer(struct sljit_compiler *compiler, tilegx_mnemonic o case TILEGX_OPC_ADD: case TILEGX_OPC_AND: case TILEGX_OPC_SUB: + case TILEGX_OPC_MULX: case TILEGX_OPC_OR: case TILEGX_OPC_XOR: case TILEGX_OPC_NOR: @@ -905,7 +905,6 @@ static SLJIT_INLINE sljit_ins * detect_jump_type(struct sljit_jump *jump, sljit_ sljit_sw diff; sljit_uw target_addr; sljit_ins *inst; - sljit_ins saved_inst; if (jump->flags & SLJIT_REWRITABLE_JUMP) return code_ptr; @@ -1009,7 +1008,7 @@ SLJIT_API_FUNC_ATTRIBUTE void * sljit_generate_code(struct sljit_compiler *compi struct sljit_const *const_; CHECK_ERROR_PTR(); - check_sljit_generate_code(compiler); + CHECK_PTR(check_sljit_generate_code(compiler)); reverse_buf(compiler); code = (sljit_ins *)SLJIT_MALLOC_EXEC(compiler->size * sizeof(sljit_ins)); @@ -1178,13 +1177,13 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_enter(struct sljit_compiler *compil sljit_si fscratches, sljit_si fsaveds, sljit_si local_size) { sljit_ins base; - sljit_ins bundle = 0; - + sljit_si i, tmp; + CHECK_ERROR(); - check_sljit_emit_enter(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size); + CHECK(check_sljit_emit_enter(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size)); set_emit_enter(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size); - local_size += (saveds + 1) * sizeof(sljit_sw); + local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1); local_size = (local_size + 7) & ~7; compiler->local_size = local_size; @@ -1200,56 +1199,52 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_enter(struct sljit_compiler *compil local_size = 0; } + /* Save the return address. */ FAIL_IF(ADDLI(ADDR_TMP_mapped, base, local_size - 8)); FAIL_IF(ST_ADD(ADDR_TMP_mapped, RA, -8)); - if (saveds >= 1) - FAIL_IF(ST_ADD(ADDR_TMP_mapped, SLJIT_SAVED_REG1_mapped, -8)); + /* Save the S registers. */ + tmp = saveds < SLJIT_NUMBER_OF_SAVED_REGISTERS ? (SLJIT_S0 + 1 - saveds) : SLJIT_FIRST_SAVED_REG; + for (i = SLJIT_S0; i >= tmp; i--) { + FAIL_IF(ST_ADD(ADDR_TMP_mapped, reg_map[i], -8)); + } - if (saveds >= 2) - FAIL_IF(ST_ADD(ADDR_TMP_mapped, SLJIT_SAVED_REG2_mapped, -8)); + /* Save the R registers that need to be reserved. */ + for (i = scratches; i >= SLJIT_FIRST_SAVED_REG; i--) { + FAIL_IF(ST_ADD(ADDR_TMP_mapped, reg_map[i], -8)); + } - if (saveds >= 3) - FAIL_IF(ST_ADD(ADDR_TMP_mapped, SLJIT_SAVED_REG3_mapped, -8)); - - if (saveds >= 4) - FAIL_IF(ST_ADD(ADDR_TMP_mapped, SLJIT_SAVED_EREG1_mapped, -8)); - - if (saveds >= 5) - FAIL_IF(ST_ADD(ADDR_TMP_mapped, SLJIT_SAVED_EREG2_mapped, -8)); - - if (args >= 1) - FAIL_IF(ADD(SLJIT_SAVED_REG1_mapped, 0, ZERO)); - - if (args >= 2) - FAIL_IF(ADD(SLJIT_SAVED_REG2_mapped, 1, ZERO)); - - if (args >= 3) - FAIL_IF(ADD(SLJIT_SAVED_REG3_mapped, 2, ZERO)); + /* Move the arguments to S registers. */ + for (i = 0; i < args; i++) { + FAIL_IF(ADD(reg_map[SLJIT_S0 - i], i, ZERO)); + } return SLJIT_SUCCESS; } -SLJIT_API_FUNC_ATTRIBUTE void sljit_set_context(struct sljit_compiler *compiler, +SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_set_context(struct sljit_compiler *compiler, sljit_si options, sljit_si args, sljit_si scratches, sljit_si saveds, sljit_si fscratches, sljit_si fsaveds, sljit_si local_size) { - CHECK_ERROR_VOID(); - check_sljit_set_context(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size); + CHECK_ERROR(); + CHECK(check_sljit_set_context(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size)); set_set_context(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size); - local_size += (saveds + 1) * sizeof(sljit_sw); + local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1); compiler->local_size = (local_size + 7) & ~7; + + return SLJIT_SUCCESS; } SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_return(struct sljit_compiler *compiler, sljit_si op, sljit_si src, sljit_sw srcw) { sljit_si local_size; sljit_ins base; - int addr_initialized = 0; + sljit_si i, tmp; + sljit_si saveds; CHECK_ERROR(); - check_sljit_emit_return(compiler, op, src, srcw); + CHECK(check_sljit_emit_return(compiler, op, src, srcw)); FAIL_IF(emit_mov_before_return(compiler, op, src, srcw)); @@ -1263,50 +1258,20 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_return(struct sljit_compiler *compi local_size = 0; } + /* Restore the return address. */ FAIL_IF(ADDLI(ADDR_TMP_mapped, base, local_size - 8)); - FAIL_IF(LD(RA, ADDR_TMP_mapped)); + FAIL_IF(LD_ADD(RA, ADDR_TMP_mapped, -8)); - if (compiler->saveds >= 5) { - FAIL_IF(ADDLI(ADDR_TMP_mapped, base, local_size - 48)); - addr_initialized = 1; - - FAIL_IF(LD_ADD(SLJIT_SAVED_EREG2_mapped, ADDR_TMP_mapped, 8)); + /* Restore the S registers. */ + saveds = compiler->saveds; + tmp = saveds < SLJIT_NUMBER_OF_SAVED_REGISTERS ? (SLJIT_S0 + 1 - saveds) : SLJIT_FIRST_SAVED_REG; + for (i = SLJIT_S0; i >= tmp; i--) { + FAIL_IF(LD_ADD(reg_map[i], ADDR_TMP_mapped, -8)); } - if (compiler->saveds >= 4) { - if (addr_initialized == 0) { - FAIL_IF(ADDLI(ADDR_TMP_mapped, base, local_size - 40)); - addr_initialized = 1; - } - - FAIL_IF(LD_ADD(SLJIT_SAVED_EREG1_mapped, ADDR_TMP_mapped, 8)); - } - - if (compiler->saveds >= 3) { - if (addr_initialized == 0) { - FAIL_IF(ADDLI(ADDR_TMP_mapped, base, local_size - 32)); - addr_initialized = 1; - } - - FAIL_IF(LD_ADD(SLJIT_SAVED_REG3_mapped, ADDR_TMP_mapped, 8)); - } - - if (compiler->saveds >= 2) { - if (addr_initialized == 0) { - FAIL_IF(ADDLI(ADDR_TMP_mapped, base, local_size - 24)); - addr_initialized = 1; - } - - FAIL_IF(LD_ADD(SLJIT_SAVED_REG2_mapped, ADDR_TMP_mapped, 8)); - } - - if (compiler->saveds >= 1) { - if (addr_initialized == 0) { - FAIL_IF(ADDLI(ADDR_TMP_mapped, base, local_size - 16)); - /* addr_initialized = 1; no need to initialize as it's the last one. */ - } - - FAIL_IF(LD_ADD(SLJIT_SAVED_REG1_mapped, ADDR_TMP_mapped, 8)); + /* Restore the R registers that need to be reserved. */ + for (i = compiler->scratches; i >= SLJIT_FIRST_SAVED_REG; i--) { + FAIL_IF(LD_ADD(reg_map[i], ADDR_TMP_mapped, -8)); } if (compiler->local_size <= SIMM_16BIT_MAX) @@ -1585,7 +1550,7 @@ static SLJIT_INLINE sljit_si emit_op_mem2(struct sljit_compiler *compiler, sljit SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_si dst, sljit_sw dstw) { CHECK_ERROR(); - check_sljit_emit_fast_enter(compiler, dst, dstw); + CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw)); ADJUST_LOCAL_OFFSET(dst, dstw); /* For UNUSED dst. Uncommon, but possible. */ @@ -1602,7 +1567,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_fast_enter(struct sljit_compiler *c SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_fast_return(struct sljit_compiler *compiler, sljit_si src, sljit_sw srcw) { CHECK_ERROR(); - check_sljit_emit_fast_return(compiler, src, srcw); + CHECK(check_sljit_emit_fast_return(compiler, src, srcw)); ADJUST_LOCAL_OFFSET(src, srcw); if (FAST_IS_REG(src)) @@ -1636,9 +1601,11 @@ static SLJIT_INLINE sljit_si emit_single_op(struct sljit_compiler *compiler, slj if (op == SLJIT_MOV_SI) return BFEXTS(reg_map[dst], reg_map[src2], 0, 31); - return BFEXTU(reg_map[dst], reg_map[src2], 0, 31); - } else if (dst != src2) - SLJIT_ASSERT_STOP(); + return BFEXTU(reg_map[dst], reg_map[src2], 0, 31); + } else if (dst != src2) { + SLJIT_ASSERT(src2 == 0); + return ADD(reg_map[dst], reg_map[src2], ZERO); + } return SLJIT_SUCCESS; @@ -1650,8 +1617,10 @@ static SLJIT_INLINE sljit_si emit_single_op(struct sljit_compiler *compiler, slj return BFEXTS(reg_map[dst], reg_map[src2], 0, 7); return BFEXTU(reg_map[dst], reg_map[src2], 0, 7); - } else if (dst != src2) - SLJIT_ASSERT_STOP(); + } else if (dst != src2) { + SLJIT_ASSERT(src2 == 0); + return ADD(reg_map[dst], reg_map[src2], ZERO); + } return SLJIT_SUCCESS; @@ -1663,8 +1632,10 @@ static SLJIT_INLINE sljit_si emit_single_op(struct sljit_compiler *compiler, slj return BFEXTS(reg_map[dst], reg_map[src2], 0, 15); return BFEXTU(reg_map[dst], reg_map[src2], 0, 15); - } else if (dst != src2) - SLJIT_ASSERT_STOP(); + } else if (dst != src2) { + SLJIT_ASSERT(src2 == 0); + return ADD(reg_map[dst], reg_map[src2], ZERO); + } return SLJIT_SUCCESS; @@ -1811,7 +1782,6 @@ static SLJIT_INLINE sljit_si emit_single_op(struct sljit_compiler *compiler, slj else { /* Rare ocasion. */ FAIL_IF(ADD(TMP_EREG2, reg_map[src1], ZERO)); - overflow_ra = TMP_EREG2; } } @@ -1903,6 +1873,17 @@ static SLJIT_INLINE sljit_si emit_single_op(struct sljit_compiler *compiler, slj return SLJIT_SUCCESS; + case SLJIT_MUL: + if (flags & SRC2_IMM) { + FAIL_IF(load_immediate(compiler, TMP_REG2_mapped, src2)); + src2 = TMP_REG2; + flags &= ~SRC2_IMM; + } + + FAIL_IF(MUL(reg_map[dst], reg_map[src1], reg_map[src2])); + + return SLJIT_SUCCESS; + #define EMIT_LOGICAL(op_imm, op_norm) \ if (flags & SRC2_IMM) { \ FAIL_IF(load_immediate(compiler, ADDR_TMP_mapped, src2)); \ @@ -1950,8 +1931,8 @@ static SLJIT_INLINE sljit_si emit_single_op(struct sljit_compiler *compiler, slj } else { \ if (op & SLJIT_SET_E) \ FAIL_IF(push_3_buffer( \ - compiler, op_imm, reg_map[dst], reg_map[src1], \ - src2 & 0x3F, __LINE__)); \ + compiler, op_norm, EQUAL_FLAG, reg_map[src1], \ + reg_map[src2], __LINE__)); \ if (CHECK_FLAGS(SLJIT_SET_E)) \ FAIL_IF(push_3_buffer( \ compiler, op_norm, reg_map[dst], reg_map[src1], \ @@ -2105,66 +2086,61 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_op_flags(struct sljit_compiler *com { sljit_si sugg_dst_ar, dst_ar; sljit_si flags = GET_ALL_FLAGS(op); + sljit_si mem_type = (op & SLJIT_INT_OP) ? (INT_DATA | SIGNED_DATA) : WORD_DATA; CHECK_ERROR(); - check_sljit_emit_op_flags(compiler, op, dst, dstw, src, srcw, type); + CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, src, srcw, type)); ADJUST_LOCAL_OFFSET(dst, dstw); if (dst == SLJIT_UNUSED) return SLJIT_SUCCESS; op = GET_OPCODE(op); + if (op == SLJIT_MOV_SI || op == SLJIT_MOV_UI) + mem_type = INT_DATA | SIGNED_DATA; sugg_dst_ar = reg_map[(op < SLJIT_ADD && FAST_IS_REG(dst)) ? dst : TMP_REG2]; compiler->cache_arg = 0; compiler->cache_argw = 0; if (op >= SLJIT_ADD && (src & SLJIT_MEM)) { ADJUST_LOCAL_OFFSET(src, srcw); - FAIL_IF(emit_op_mem2(compiler, WORD_DATA | LOAD_DATA, TMP_REG1_mapped, src, srcw, dst, dstw)); + FAIL_IF(emit_op_mem2(compiler, mem_type | LOAD_DATA, TMP_REG1_mapped, src, srcw, dst, dstw)); src = TMP_REG1; srcw = 0; } - switch (type) { - case SLJIT_C_EQUAL: - case SLJIT_C_NOT_EQUAL: + switch (type & 0xff) { + case SLJIT_EQUAL: + case SLJIT_NOT_EQUAL: FAIL_IF(CMPLTUI(sugg_dst_ar, EQUAL_FLAG, 1)); dst_ar = sugg_dst_ar; break; - case SLJIT_C_LESS: - case SLJIT_C_GREATER_EQUAL: - case SLJIT_C_FLOAT_LESS: - case SLJIT_C_FLOAT_GREATER_EQUAL: + case SLJIT_LESS: + case SLJIT_GREATER_EQUAL: dst_ar = ULESS_FLAG; break; - case SLJIT_C_GREATER: - case SLJIT_C_LESS_EQUAL: - case SLJIT_C_FLOAT_GREATER: - case SLJIT_C_FLOAT_LESS_EQUAL: + case SLJIT_GREATER: + case SLJIT_LESS_EQUAL: dst_ar = UGREATER_FLAG; break; - case SLJIT_C_SIG_LESS: - case SLJIT_C_SIG_GREATER_EQUAL: + case SLJIT_SIG_LESS: + case SLJIT_SIG_GREATER_EQUAL: dst_ar = LESS_FLAG; break; - case SLJIT_C_SIG_GREATER: - case SLJIT_C_SIG_LESS_EQUAL: + case SLJIT_SIG_GREATER: + case SLJIT_SIG_LESS_EQUAL: dst_ar = GREATER_FLAG; break; - case SLJIT_C_OVERFLOW: - case SLJIT_C_NOT_OVERFLOW: + case SLJIT_OVERFLOW: + case SLJIT_NOT_OVERFLOW: dst_ar = OVERFLOW_FLAG; break; - case SLJIT_C_MUL_OVERFLOW: - case SLJIT_C_MUL_NOT_OVERFLOW: + case SLJIT_MUL_OVERFLOW: + case SLJIT_MUL_NOT_OVERFLOW: FAIL_IF(CMPLTUI(sugg_dst_ar, OVERFLOW_FLAG, 1)); dst_ar = sugg_dst_ar; type ^= 0x1; /* Flip type bit for the XORI below. */ break; - case SLJIT_C_FLOAT_EQUAL: - case SLJIT_C_FLOAT_NOT_EQUAL: - dst_ar = EQUAL_FLAG; - break; default: SLJIT_ASSERT_STOP(); @@ -2180,11 +2156,11 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_op_flags(struct sljit_compiler *com if (op >= SLJIT_ADD) { if (TMP_REG2_mapped != dst_ar) FAIL_IF(ADD(TMP_REG2_mapped, dst_ar, ZERO)); - return emit_op(compiler, op | flags, CUMULATIVE_OP | LOGICAL_OP | IMM_OP | ALT_KEEP_CACHE, dst, dstw, src, srcw, TMP_REG2, 0); + return emit_op(compiler, op | flags, mem_type | CUMULATIVE_OP | LOGICAL_OP | IMM_OP | ALT_KEEP_CACHE, dst, dstw, src, srcw, TMP_REG2, 0); } if (dst & SLJIT_MEM) - return emit_op_mem(compiler, WORD_DATA, dst_ar, dst, dstw); + return emit_op_mem(compiler, mem_type, dst_ar, dst, dstw); if (sugg_dst_ar != dst_ar) return ADD(sugg_dst_ar, dst_ar, ZERO); @@ -2194,7 +2170,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_op_flags(struct sljit_compiler *com SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_op0(struct sljit_compiler *compiler, sljit_si op) { CHECK_ERROR(); - check_sljit_emit_op0(compiler, op); + CHECK(check_sljit_emit_op0(compiler, op)); op = GET_OPCODE(op); switch (op) { @@ -2204,10 +2180,10 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_op0(struct sljit_compiler *compiler case SLJIT_BREAKPOINT: return PI(BPT); - case SLJIT_UMUL: - case SLJIT_SMUL: - case SLJIT_UDIV: - case SLJIT_SDIV: + case SLJIT_LUMUL: + case SLJIT_LSMUL: + case SLJIT_UDIVI: + case SLJIT_SDIVI: SLJIT_ASSERT_STOP(); } @@ -2217,7 +2193,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_op0(struct sljit_compiler *compiler SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_op1(struct sljit_compiler *compiler, sljit_si op, sljit_si dst, sljit_sw dstw, sljit_si src, sljit_sw srcw) { CHECK_ERROR(); - check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw); + CHECK(check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw)); ADJUST_LOCAL_OFFSET(dst, dstw); ADJUST_LOCAL_OFFSET(src, srcw); @@ -2273,7 +2249,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_op1(struct sljit_compiler *compiler return emit_op(compiler, SLJIT_SUB | GET_ALL_FLAGS(op), IMM_OP, dst, dstw, SLJIT_IMM, 0, src, srcw); case SLJIT_CLZ: - return emit_op(compiler, op, 0, dst, dstw, TMP_REG1, 0, src, srcw); + return emit_op(compiler, op, (op & SLJIT_INT_OP) ? INT_DATA : WORD_DATA, dst, dstw, TMP_REG1, 0, src, srcw); } return SLJIT_SUCCESS; @@ -2282,7 +2258,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_op1(struct sljit_compiler *compiler SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_op2(struct sljit_compiler *compiler, sljit_si op, sljit_si dst, sljit_sw dstw, sljit_si src1, sljit_sw src1w, sljit_si src2, sljit_sw src2w) { CHECK_ERROR(); - check_sljit_emit_op2(compiler, op, dst, dstw, src1, src1w, src2, src2w); + CHECK(check_sljit_emit_op2(compiler, op, dst, dstw, src1, src1w, src2, src2w)); ADJUST_LOCAL_OFFSET(dst, dstw); ADJUST_LOCAL_OFFSET(src1, src1w); ADJUST_LOCAL_OFFSET(src2, src2w); @@ -2325,7 +2301,7 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_label * sljit_emit_label(struct sljit_comp flush_buffer(compiler); CHECK_ERROR_PTR(); - check_sljit_emit_label(compiler); + CHECK_PTR(check_sljit_emit_label(compiler)); if (compiler->last_label && compiler->last_label->size == compiler->size) return compiler->last_label; @@ -2344,7 +2320,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_ijump(struct sljit_compiler *compil flush_buffer(compiler); CHECK_ERROR(); - check_sljit_emit_ijump(compiler, type, src, srcw); + CHECK(check_sljit_emit_ijump(compiler, type, src, srcw)); ADJUST_LOCAL_OFFSET(src, srcw); if (FAST_IS_REG(src)) { @@ -2404,8 +2380,10 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_ijump(struct sljit_compiler *compil return SLJIT_SUCCESS; - } else if (src & SLJIT_MEM) + } else if (src & SLJIT_MEM) { FAIL_IF(emit_op(compiler, SLJIT_MOV, WORD_DATA, TMP_REG2, 0, TMP_REG1, 0, src, srcw)); + flush_buffer(compiler); + } FAIL_IF(JR_SOLO(reg_map[src_r])); @@ -2432,7 +2410,7 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump * sljit_emit_jump(struct sljit_compil flush_buffer(compiler); CHECK_ERROR_PTR(); - check_sljit_emit_jump(compiler, type); + CHECK_PTR(check_sljit_emit_jump(compiler, type)); jump = (struct sljit_jump *)ensure_abuf(compiler, sizeof(struct sljit_jump)); PTR_FAIL_IF(!jump); @@ -2440,48 +2418,42 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump * sljit_emit_jump(struct sljit_compil type &= 0xff; switch (type) { - case SLJIT_C_EQUAL: - case SLJIT_C_FLOAT_NOT_EQUAL: + case SLJIT_EQUAL: BR_NZ(EQUAL_FLAG); break; - case SLJIT_C_NOT_EQUAL: - case SLJIT_C_FLOAT_EQUAL: + case SLJIT_NOT_EQUAL: BR_Z(EQUAL_FLAG); break; - case SLJIT_C_LESS: - case SLJIT_C_FLOAT_LESS: + case SLJIT_LESS: BR_Z(ULESS_FLAG); break; - case SLJIT_C_GREATER_EQUAL: - case SLJIT_C_FLOAT_GREATER_EQUAL: + case SLJIT_GREATER_EQUAL: BR_NZ(ULESS_FLAG); break; - case SLJIT_C_GREATER: - case SLJIT_C_FLOAT_GREATER: + case SLJIT_GREATER: BR_Z(UGREATER_FLAG); break; - case SLJIT_C_LESS_EQUAL: - case SLJIT_C_FLOAT_LESS_EQUAL: + case SLJIT_LESS_EQUAL: BR_NZ(UGREATER_FLAG); break; - case SLJIT_C_SIG_LESS: + case SLJIT_SIG_LESS: BR_Z(LESS_FLAG); break; - case SLJIT_C_SIG_GREATER_EQUAL: + case SLJIT_SIG_GREATER_EQUAL: BR_NZ(LESS_FLAG); break; - case SLJIT_C_SIG_GREATER: + case SLJIT_SIG_GREATER: BR_Z(GREATER_FLAG); break; - case SLJIT_C_SIG_LESS_EQUAL: + case SLJIT_SIG_LESS_EQUAL: BR_NZ(GREATER_FLAG); break; - case SLJIT_C_OVERFLOW: - case SLJIT_C_MUL_OVERFLOW: + case SLJIT_OVERFLOW: + case SLJIT_MUL_OVERFLOW: BR_Z(OVERFLOW_FLAG); break; - case SLJIT_C_NOT_OVERFLOW: - case SLJIT_C_MUL_NOT_OVERFLOW: + case SLJIT_NOT_OVERFLOW: + case SLJIT_MUL_NOT_OVERFLOW: BR_NZ(OVERFLOW_FLAG); break; default: @@ -2536,7 +2508,7 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_const * sljit_emit_const(struct sljit_comp flush_buffer(compiler); CHECK_ERROR_PTR(); - check_sljit_emit_const(compiler, dst, dstw, init_value); + CHECK_PTR(check_sljit_emit_const(compiler, dst, dstw, init_value)); ADJUST_LOCAL_OFFSET(dst, dstw); const_ = (struct sljit_const *)ensure_abuf(compiler, sizeof(struct sljit_const)); @@ -2572,3 +2544,18 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_consta inst[3] = (inst[3] & ~(0xFFFFL << 43)) | ((new_constant & 0xFFFFL) << 43); SLJIT_CACHE_FLUSH(inst, inst + 4); } + +SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_get_register_index(sljit_si reg) +{ + CHECK_REG_INDEX(check_sljit_get_register_index(reg)); + return reg_map[reg]; +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_op_custom(struct sljit_compiler *compiler, + void *instruction, sljit_si size) +{ + CHECK_ERROR(); + CHECK(check_sljit_emit_op_custom(compiler, instruction, size)); + return SLJIT_ERR_UNSUPPORTED; +} + diff --git a/pcre2-10.20/src/sljit/sljitNativeX86_32.c b/pcre2-10.21/src/sljit/sljitNativeX86_32.c similarity index 100% rename from pcre2-10.20/src/sljit/sljitNativeX86_32.c rename to pcre2-10.21/src/sljit/sljitNativeX86_32.c diff --git a/pcre2-10.20/src/sljit/sljitNativeX86_64.c b/pcre2-10.21/src/sljit/sljitNativeX86_64.c similarity index 100% rename from pcre2-10.20/src/sljit/sljitNativeX86_64.c rename to pcre2-10.21/src/sljit/sljitNativeX86_64.c diff --git a/pcre2-10.20/src/sljit/sljitNativeX86_common.c b/pcre2-10.21/src/sljit/sljitNativeX86_common.c similarity index 97% rename from pcre2-10.20/src/sljit/sljitNativeX86_common.c rename to pcre2-10.21/src/sljit/sljitNativeX86_common.c index b7bbb0384..416c15afa 100644 --- a/pcre2-10.20/src/sljit/sljitNativeX86_common.c +++ b/pcre2-10.21/src/sljit/sljitNativeX86_common.c @@ -273,7 +273,9 @@ static sljit_si cpu_has_sse2 = -1; #endif static sljit_si cpu_has_cmov = -1; -#if defined(_MSC_VER) && _MSC_VER >= 1400 +#ifdef _WIN32_WCE +#include +#elif defined(_MSC_VER) && _MSC_VER >= 1400 #include #endif @@ -2934,3 +2936,69 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_consta { *(sljit_sw*)addr = new_constant; } + +SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_x86_is_sse2_available(void) +{ +#if (defined SLJIT_DETECT_SSE2 && SLJIT_DETECT_SSE2) + if (cpu_has_sse2 == -1) + get_cpu_features(); + return cpu_has_sse2; +#else + return 1; +#endif +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_x86_is_cmov_available(void) +{ + if (cpu_has_cmov == -1) + get_cpu_features(); + return cpu_has_cmov; +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_x86_emit_cmov(struct sljit_compiler *compiler, + sljit_si type, + sljit_si dst_reg, + sljit_si src, sljit_sw srcw) +{ + sljit_ub* inst; + + CHECK_ERROR(); +#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + CHECK_ARGUMENT(sljit_x86_is_cmov_available()); + CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_INT_OP))); + CHECK_ARGUMENT((type & 0xff) >= SLJIT_EQUAL && (type & 0xff) <= SLJIT_D_ORDERED); + CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(dst_reg & ~SLJIT_INT_OP)); + FUNCTION_CHECK_SRC(src, srcw); +#endif +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + if (SLJIT_UNLIKELY(!!compiler->verbose)) { + fprintf(compiler->verbose, " x86_cmov%s %s%s, ", + !(dst_reg & SLJIT_INT_OP) ? "" : ".i", + JUMP_PREFIX(type), jump_names[type & 0xff]); + sljit_verbose_reg(compiler, dst_reg & ~SLJIT_INT_OP); + fprintf(compiler->verbose, ", "); + sljit_verbose_param(compiler, src, srcw); + fprintf(compiler->verbose, "\n"); + } +#endif + + ADJUST_LOCAL_OFFSET(src, srcw); + CHECK_EXTRA_REGS(src, srcw, (void)0); + +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + compiler->mode32 = dst_reg & SLJIT_INT_OP; +#endif + dst_reg &= ~SLJIT_INT_OP; + + if (SLJIT_UNLIKELY(src & SLJIT_IMM)) { + EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_IMM, srcw); + src = TMP_REG1; + srcw = 0; + } + + inst = emit_x86_instruction(compiler, 2, dst_reg, 0, src, srcw); + FAIL_IF(!inst); + *inst++ = GROUP_0F; + *inst = get_jump_code(type & 0xff) - 0x40; + return SLJIT_SUCCESS; +} diff --git a/pcre2-10.20/src/sljit/sljitUtils.c b/pcre2-10.21/src/sljit/sljitUtils.c similarity index 100% rename from pcre2-10.20/src/sljit/sljitUtils.c rename to pcre2-10.21/src/sljit/sljitUtils.c diff --git a/pcre2-10.20/test-driver b/pcre2-10.21/test-driver similarity index 100% rename from pcre2-10.20/test-driver rename to pcre2-10.21/test-driver From a10a79c6d09c468d78d31bb7b444c8acefe44f5f Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Mon, 18 Apr 2016 19:25:12 -0700 Subject: [PATCH 112/363] restyle builtin module to match project style Reduces lint errors from 271 to 215 (-21%). Line count from 4304 to 3242 (-25%). Another step in resolving issue #2902. --- src/builtin.cpp | 3772 +++++++++++++++++------------------------------ src/builtin.h | 160 +- 2 files changed, 1435 insertions(+), 2497 deletions(-) diff --git a/src/builtin.cpp b/src/builtin.cpp index 86213a762..87b69b6c2 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -1,186 +1,146 @@ -/** \file builtin.c - Functions for executing builtin functions. +// Functions for executing builtin functions. +// +// How to add a new builtin function: +// +// 1). Create a function in builtin.c with the following signature: +// +// static int builtin_NAME( parser_t &parser, wchar_t ** args ) +// +// where NAME is the name of the builtin, and args is a zero-terminated list of arguments. +// +// 2). Add a line like { L"NAME", &builtin_NAME, N_(L"Bla bla bla") }, to the builtin_data_t +// variable. The description is used by the completion system. Note that this array is sorted. +// +// 3). Create a file doc_src/NAME.txt, containing the manual for the builtin in Doxygen-format. +// Check the other builtin manuals for proper syntax. +// +// 4). Use 'git add doc_src/NAME.txt' to start tracking changes to the documentation file. +#include "config.h" // IWYU pragma: keep - How to add a new builtin function: - - 1). Create a function in builtin.c with the following signature: - - static int builtin_NAME( parser_t &parser, wchar_t ** args ) - - where NAME is the name of the builtin, and args is a zero-terminated list of arguments. - - 2). Add a line like { L"NAME", &builtin_NAME, N_(L"Bla bla bla") }, to the builtin_data_t variable. The description is used by the completion system. Note that this array is sorted! - - 3). Create a file doc_src/NAME.txt, containing the manual for the builtin in Doxygen-format. Check the other builtin manuals for proper syntax. - - 4). Use 'git add doc_src/NAME.txt' to start tracking changes to the documentation file. - -*/ - -#include "config.h" // IWYU pragma: keep - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include +#include #include #include -#include "fallback.h" // IWYU pragma: keep +#include "fallback.h" // IWYU pragma: keep -#include "wutil.h" #include "builtin.h" -#include "function.h" #include "complete.h" -#include "proc.h" -#include "parser.h" -#include "reader.h" #include "env.h" -#include "wgetopt.h" -#include "tokenizer.h" +#include "event.h" +#include "exec.h" +#include "expand.h" +#include "function.h" +#include "highlight.h" +#include "history.h" #include "input.h" #include "intern.h" -#include "event.h" -#include "signal.h" -#include "exec.h" -#include "highlight.h" -#include "parse_util.h" -#include "parser_keywords.h" -#include "expand.h" -#include "path.h" -#include "history.h" -#include "parse_tree.h" #include "parse_constants.h" +#include "parse_tree.h" +#include "parse_util.h" +#include "parser.h" +#include "parser_keywords.h" +#include "path.h" +#include "proc.h" +#include "reader.h" +#include "signal.h" +#include "tokenizer.h" #include "wcstringutil.h" +#include "wgetopt.h" +#include "wutil.h" -/** - The default prompt for the read command -*/ +// The default prompt for the read command. #define DEFAULT_READ_PROMPT L"set_color green; echo -n read; set_color normal; echo -n \"> \"" -/** - The mode name to pass to history and input -*/ - +// The mode name to pass to history and input. #define READ_MODE_NAME L"fish_read" -/** - The send stuff to foreground message -*/ -#define FG_MSG _( L"Send job %d, '%ls' to foreground\n" ) +// The send stuff to foreground message. +#define FG_MSG _(L"Send job %d, '%ls' to foreground\n") -/** - Datastructure to describe a builtin. -*/ -struct builtin_data_t -{ - /** - Name of the builtin - */ +// Datastructure to describe a builtin. +struct builtin_data_t { + // Name of the builtin. const wchar_t *name; - /** - Function pointer tothe builtin implementation - */ + // Function pointer tothe builtin implementation. int (*func)(parser_t &parser, io_streams_t &streams, wchar_t **argv); - /** - Description of what the builtin does - */ + // Description of what the builtin does. const wchar_t *desc; bool operator<(const wcstring &) const; bool operator<(const builtin_data_t *) const; }; -bool builtin_data_t::operator<(const wcstring &other) const -{ +bool builtin_data_t::operator<(const wcstring &other) const { return wcscmp(this->name, other.c_str()) < 0; } -bool builtin_data_t::operator<(const builtin_data_t *other) const -{ +bool builtin_data_t::operator<(const builtin_data_t *other) const { return wcscmp(this->name, other->name) < 0; } -/** - Counts the number of non null pointers in the specified array -*/ -int builtin_count_args(const wchar_t * const * argv) -{ +// Counts the number of non null pointers in the specified array. +int builtin_count_args(const wchar_t *const *argv) { int argc = 1; - while (argv[argc] != NULL) - { + while (argv[argc] != NULL) { argc++; } return argc; } -/** - This function works like wperror, but it prints its result into - the streams.err string instead of to stderr. Used by the builtin - commands. -*/ - -static void builtin_wperror(const wchar_t *s, io_streams_t &streams) -{ +// This function works like wperror, but it prints its result into the streams.err string instead of +// to stderr. Used by the builtin commands. +static void builtin_wperror(const wchar_t *s, io_streams_t &streams) { char *err = strerror(errno); - if (s != NULL) - { + if (s != NULL) { streams.err.append(s); streams.err.append(L": "); } - if (err != NULL) - { + if (err != NULL) { const wcstring werr = str2wcstring(err); streams.err.append(werr); streams.err.push_back(L'\n'); } } -/** - Count the number of times the specified character occurs in the specified string -*/ -static int count_char(const wchar_t *str, wchar_t c) -{ +// Count the number of times the specified character occurs in the specified string. +static int count_char(const wchar_t *str, wchar_t c) { int res = 0; - for (; *str; str++) - { - res += (*str==c); + for (; *str; str++) { + res += (*str == c); } return res; } -wcstring builtin_help_get(parser_t &parser, io_streams_t &streams, const wchar_t *name) -{ - /* This won't ever work if no_exec is set */ - if (no_exec) - return wcstring(); +wcstring builtin_help_get(parser_t &parser, io_streams_t &streams, const wchar_t *name) { + // This won't ever work if no_exec is set. + if (no_exec) return wcstring(); wcstring_list_t lst; wcstring out; const wcstring name_esc = escape_string(name, 1); wcstring cmd = format_string(L"__fish_print_help %ls", name_esc.c_str()); - if (!streams.out_is_redirected && isatty(STDOUT_FILENO)) - { + if (!streams.out_is_redirected && isatty(STDOUT_FILENO)) { // since we're using a subshell, __fish_print_help can't tell we're in // a terminal. Tell it ourselves. int cols = common_get_width(); cmd = format_string(L"__fish_print_help --tty-width %d %ls", cols, name_esc.c_str()); } - if (exec_subshell(cmd, lst, false /* don't apply exit status */) >= 0) - { - for (size_t i=0; i= 0) { + for (size_t i = 0; i < lst.size(); i++) { out.append(lst.at(i)); out.push_back(L'\n'); } @@ -188,225 +148,165 @@ wcstring builtin_help_get(parser_t &parser, io_streams_t &streams, const wchar_t return out; } -/** - Print help for the specified builtin. If \c b is sb_err, also print - the line information - - If \c b is the buffer representing standard error, and the help - message is about to be printed to an interactive screen, it may be - shortened to fit the screen. - - -*/ - -void builtin_print_help(parser_t &parser, io_streams_t &streams, const wchar_t *cmd, output_stream_t &b) -{ +// Print help for the specified builtin. If \c b is sb_err, also print the line information. +// +// If \c b is the buffer representing standard error, and the help message is about to be printed to +// an interactive screen, it may be shortened to fit the screen. +void builtin_print_help(parser_t &parser, io_streams_t &streams, const wchar_t *cmd, + output_stream_t &b) { bool is_stderr = (&b == &streams.err); - if (is_stderr) - { + if (is_stderr) { b.append(parser.current_line()); } - + const wcstring h = builtin_help_get(parser, streams, cmd); - - if (!h.size()) - return; - + + if (!h.size()) return; + wchar_t *str = wcsdup(h.c_str()); - if (str) - { + if (str) { bool is_short = false; - if (is_stderr) - { - - /* - Interactive mode help to screen - only print synopsis if - the rest won't fit - */ - + if (is_stderr) { + // 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)) - { + if (!get_is_interactive() || (lines > 2 * screen_height / 3)) { wchar_t *pos; - int cut=0; + int cut = 0; int i; - + is_short = true; - - /* - First move down 4 lines - */ - + + // First move down 4 lines. pos = str; - for (i=0; (i<4) && pos && *pos; i++) - { - pos = wcschr(pos+1, L'\n'); + 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') - { + + 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' ') - { + + 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'; + 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) - { + + // 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) { b.append_format(_(L"%ls: Type 'help %ls' for related documentation\n\n"), cmd, cmd); } - + free(str); } } - -/** - Perform error reporting for encounter with unknown option -*/ -static void builtin_unknown_option(parser_t &parser, io_streams_t &streams, const wchar_t *cmd, const wchar_t *opt) -{ +// Perform error reporting for encounter with unknown option. +static void builtin_unknown_option(parser_t &parser, io_streams_t &streams, const wchar_t *cmd, + const wchar_t *opt) { streams.err.append_format(BUILTIN_ERR_UNKNOWN, cmd, opt); builtin_print_help(parser, streams, cmd, streams.err); } -/** - Perform error reporting for encounter with missing argument -*/ -static void builtin_missing_argument(parser_t &parser, io_streams_t &streams, const wchar_t *cmd, const wchar_t *opt) -{ +// Perform error reporting for encounter with missing argument. +static void builtin_missing_argument(parser_t &parser, io_streams_t &streams, const wchar_t *cmd, + const wchar_t *opt) { streams.err.append_format(BUILTIN_ERR_MISSING, cmd, opt); builtin_print_help(parser, streams, cmd, streams.err); } -/* - Here follows the definition of all builtin commands. The function - names are all on the form builtin_NAME where NAME is the name of the - builtin. so the function name for the builtin 'fg' is - 'builtin_fg'. - - A few builtins, including 'while', 'command' and 'builtin' are not - defined here as they are handled directly by the parser. (They are - not parsed as commands, instead they only alter the parser state) - - The builtins 'break' and 'continue' are so closely related that they - share the same implementation, namely 'builtin_break_continue. - - Several other builtins, including jobs, ulimit and set are so big - that they have been given their own file. These files are all named - 'builtin_NAME.c', where NAME is the name of the builtin. These files - are included directly below. - -*/ - - -#include "builtin_set.cpp" +// Here follows the definition of all builtin commands. The function names are all on the form +// builtin_NAME where NAME is the name of the builtin. so the function name for the builtin 'fg' is +// 'builtin_fg'. +// +// A few builtins, including 'while', 'command' and 'builtin' are not defined here as they are +// handled directly by the parser. (They are not parsed as commands, instead they only alter the +// parser state) +// +// The builtins 'break' and 'continue' are so closely related that they share the same +// implementation, namely 'builtin_break_continue. +// +// Several other builtins, including jobs, ulimit and set are so big that they have been given their +// own file. These files are all named 'builtin_NAME.c', where NAME is the name of the builtin. +// These files are included directly below. #include "builtin_commandline.cpp" #include "builtin_complete.cpp" -#include "builtin_ulimit.cpp" #include "builtin_jobs.cpp" -#include "builtin_set_color.cpp" #include "builtin_printf.cpp" +#include "builtin_set.cpp" +#include "builtin_set_color.cpp" +#include "builtin_ulimit.cpp" -/* builtin_test lives in builtin_test.cpp */ +// builtin_test lives in builtin_test.cpp int builtin_test(parser_t &parser, io_streams_t &streams, wchar_t **argv); -/* builtin_string lives in builtin_string.cpp */ +// builtin_string lives in builtin_string.cpp int builtin_string(parser_t &parser, io_streams_t &streams, wchar_t **argv); -/** - List a single key binding. - Returns false if no binding with that sequence and mode exists. - */ -static bool builtin_bind_list_one(const wcstring &seq, const wcstring &bind_mode, io_streams_t &streams) -{ +// List a single key binding. +// Returns false if no binding with that sequence and mode exists. +static bool builtin_bind_list_one(const wcstring &seq, const wcstring &bind_mode, + io_streams_t &streams) { std::vector ecmds; wcstring sets_mode; - if (!input_mapping_get(seq, bind_mode, &ecmds, &sets_mode)) - { + if (!input_mapping_get(seq, bind_mode, &ecmds, &sets_mode)) { return false; } streams.out.append(L"bind"); - // Append the mode flags if applicable - if (bind_mode != DEFAULT_BIND_MODE) - { + // Append the mode flags if applicable. + if (bind_mode != DEFAULT_BIND_MODE) { const wcstring emode = escape_string(bind_mode, ESCAPE_ALL); streams.out.append(L" -M "); streams.out.append(emode); } - if (sets_mode != bind_mode) - { + if (sets_mode != bind_mode) { const wcstring esets_mode = escape_string(sets_mode, ESCAPE_ALL); streams.out.append(L" -m "); streams.out.append(esets_mode); } - // Append the name + // Append the name. wcstring tname; - if (input_terminfo_get_name(seq, &tname)) - { - // Note that we show -k here because we have an input key name - streams.out.append_format( L" -k %ls", tname.c_str()); - } - else - { - // No key name, so no -k; we show the escape sequence directly + if (input_terminfo_get_name(seq, &tname)) { + // Note that we show -k here because we have an input key name. + streams.out.append_format(L" -k %ls", tname.c_str()); + } else { + // No key name, so no -k; we show the escape sequence directly. const wcstring eseq = escape_string(seq, ESCAPE_ALL); - streams.out.append_format( L" %ls", eseq.c_str()); + streams.out.append_format(L" %ls", eseq.c_str()); } - // Now show the list of commands - for (size_t i = 0; i < ecmds.size(); i++) - { + // Now show the list of commands. + for (size_t i = 0; i < ecmds.size(); i++) { const wcstring &ecmd = ecmds.at(i); const wcstring escaped_ecmd = escape_string(ecmd, ESCAPE_ALL); streams.out.push_back(' '); @@ -417,19 +317,13 @@ static bool builtin_bind_list_one(const wcstring &seq, const wcstring &bind_mode return true; } -/** - List all current key bindings - */ -static void builtin_bind_list(const wchar_t *bind_mode, io_streams_t &streams) -{ +// List all current key bindings. +static void builtin_bind_list(const wchar_t *bind_mode, io_streams_t &streams) { const std::vector lst = input_mapping_get_names(); for (std::vector::const_iterator it = lst.begin(), end = lst.end(); - it != end; - ++it) - { - if (bind_mode != NULL && bind_mode != it->mode) - { + it != end; ++it) { + if (bind_mode != NULL && bind_mode != it->mode) { continue; } @@ -437,146 +331,106 @@ static void builtin_bind_list(const wchar_t *bind_mode, io_streams_t &streams) } } -/** - Print terminfo key binding names to string buffer used for standard output. - - \param all if set, all terminfo key binding names will be - printed. If not set, only ones that are defined for this terminal - are printed. - */ -static void builtin_bind_key_names(int all, io_streams_t &streams) -{ +// Print terminfo key binding names to string buffer used for standard output. +// +// \param all if set, all terminfo key binding names will be printed. If not set, only ones that +// are defined for this terminal are printed. +static void builtin_bind_key_names(int all, io_streams_t &streams) { const wcstring_list_t names = input_terminfo_get_names(!all); - for (size_t i=0; i lst = input_mapping_get_names(); for (std::vector::const_iterator it = lst.begin(), end = lst.end(); - it != end; - ++it) - { - if (mode == NULL || mode == it->mode) - { + it != end; ++it) { + if (mode == NULL || mode == it->mode) { input_mapping_erase(it->seq, it->mode); } } return 0; - } - else - { + } else { int res = 0; if (mode == NULL) mode = DEFAULT_BIND_MODE; - while (*seq) - { - if (use_terminfo) - { + while (*seq) { + if (use_terminfo) { wcstring seq2; - if (get_terminfo_sequence(*seq++, &seq2, streams)) - { + if (get_terminfo_sequence(*seq++, &seq2, streams)) { input_mapping_erase(seq2, mode); - } - else - { + } else { res = 1; } - } - else - { + } else { input_mapping_erase(*seq++, mode); } } @@ -585,205 +439,152 @@ static int builtin_bind_erase(wchar_t **seq, int all, const wchar_t *mode, int u } } - -/** - The bind builtin, used for setting character sequences -*/ -static int builtin_bind(parser_t &parser, io_streams_t &streams, wchar_t **argv) -{ +// The bind builtin, used for setting character sequences. +static int builtin_bind(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wgetopter_t w; - enum - { - BIND_INSERT, - BIND_ERASE, - BIND_KEY_NAMES, - BIND_FUNCTION_NAMES - }; - - int argc=builtin_count_args(argv); + enum { BIND_INSERT, BIND_ERASE, BIND_KEY_NAMES, BIND_FUNCTION_NAMES }; + int argc = builtin_count_args(argv); int mode = BIND_INSERT; int res = STATUS_BUILTIN_OK; int all = 0; - const wchar_t *bind_mode = DEFAULT_BIND_MODE; bool bind_mode_given = false; const wchar_t *sets_bind_mode = DEFAULT_BIND_MODE; bool sets_bind_mode_given = false; - int use_terminfo = 0; - w.woptind=0; + w.woptind = 0; - static const struct woption long_options[] = - { - { L"all", no_argument, 0, 'a' }, - { L"erase", no_argument, 0, 'e' }, - { L"function-names", no_argument, 0, 'f' }, - { L"help", no_argument, 0, 'h' }, - { L"key", no_argument, 0, 'k' }, - { L"key-names", no_argument, 0, 'K' }, - { L"mode", required_argument, 0, 'M' }, - { L"sets-mode", required_argument, 0, 'm' }, - { 0, 0, 0, 0 } - }; + static const struct woption long_options[] = {{L"all", no_argument, 0, 'a'}, + {L"erase", no_argument, 0, 'e'}, + {L"function-names", no_argument, 0, 'f'}, + {L"help", no_argument, 0, 'h'}, + {L"key", no_argument, 0, 'k'}, + {L"key-names", no_argument, 0, 'K'}, + {L"mode", required_argument, 0, 'M'}, + {L"sets-mode", required_argument, 0, 'm'}, + {0, 0, 0, 0}}; - while (1) - { + while (1) { int opt_index = 0; - int opt = w.wgetopt_long(argc, - argv, - L"aehkKfM:m:", - long_options, - &opt_index); + int opt = w.wgetopt_long(argc, argv, L"aehkKfM:m:", long_options, &opt_index); - if (opt == -1) - break; + if (opt == -1) break; - switch (opt) - { - case 0: - if (long_options[opt_index].flag != 0) - break; - streams.err.append_format(BUILTIN_ERR_UNKNOWN, - argv[0], - long_options[opt_index].name); + switch (opt) { + case 0: { + if (long_options[opt_index].flag != 0) break; + streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0], + long_options[opt_index].name); builtin_print_help(parser, streams, argv[0], streams.err); return STATUS_BUILTIN_ERROR; - - case 'a': + } + case 'a': { all = 1; break; - - case 'e': + } + case 'e': { mode = BIND_ERASE; break; - - case 'h': + } + case 'h': { builtin_print_help(parser, streams, argv[0], streams.out); return STATUS_BUILTIN_OK; - - case 'k': + } + case 'k': { use_terminfo = 1; break; - - case 'K': + } + case 'K': { mode = BIND_KEY_NAMES; break; - - case 'f': + } + case 'f': { mode = BIND_FUNCTION_NAMES; break; - - case 'M': + } + case 'M': { bind_mode = w.woptarg; bind_mode_given = true; break; - - case 'm': + } + case 'm': { sets_bind_mode = w.woptarg; sets_bind_mode_given = true; break; - - case '?': - builtin_unknown_option(parser, streams, argv[0], argv[w.woptind-1]); + } + case '?': { + builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]); return STATUS_BUILTIN_ERROR; - - + } } } - /* - * if mode is given, but not new mode, default to new mode to mode - */ - if (bind_mode_given && !sets_bind_mode_given) - { + // if mode is given, but not new mode, default to new mode to mode. + if (bind_mode_given && !sets_bind_mode_given) { sets_bind_mode = bind_mode; } - switch (mode) - { - - case BIND_ERASE: - { - if (builtin_bind_erase(&argv[w.woptind], all, bind_mode_given ? bind_mode : NULL, use_terminfo, streams)) - { + switch (mode) { + case BIND_ERASE: { + if (builtin_bind_erase(&argv[w.woptind], all, bind_mode_given ? bind_mode : NULL, + use_terminfo, streams)) { res = STATUS_BUILTIN_ERROR; } break; } - - case BIND_INSERT: - { - switch (argc-w.woptind) - { - case 0: - { + case BIND_INSERT: { + switch (argc - w.woptind) { + case 0: { builtin_bind_list(bind_mode_given ? bind_mode : NULL, streams); break; } - - case 1: - { + case 1: { wcstring seq; - if (use_terminfo) - { - if (!get_terminfo_sequence(argv[w.woptind], &seq, streams)) - { + if (use_terminfo) { + if (!get_terminfo_sequence(argv[w.woptind], &seq, streams)) { res = STATUS_BUILTIN_ERROR; - // get_terminfo_sequence already printed the error + // get_terminfo_sequence already printed the error. break; } - } - else - { + } else { seq = argv[w.woptind]; } - if (!builtin_bind_list_one(seq, bind_mode, streams)) - { + if (!builtin_bind_list_one(seq, bind_mode, streams)) { res = STATUS_BUILTIN_ERROR; wcstring eseq = escape_string(argv[w.woptind], 0); - if (use_terminfo) - { - streams.err.append_format(_(L"%ls: No binding found for key '%ls'\n"), argv[0], eseq.c_str()); - } - else - { - streams.err.append_format(_(L"%ls: No binding found for sequence '%ls'\n"), argv[0], eseq.c_str()); + if (use_terminfo) { + streams.err.append_format(_(L"%ls: No binding found for key '%ls'\n"), + argv[0], eseq.c_str()); + } else { + streams.err.append_format( + _(L"%ls: No binding found for sequence '%ls'\n"), argv[0], + eseq.c_str()); } } break; } - - default: - { - if (builtin_bind_add(argv[w.woptind], argv + (w.woptind + 1), argc - (w.woptind + 1), bind_mode, sets_bind_mode, use_terminfo, streams)) - { + default: { + if (builtin_bind_add(argv[w.woptind], argv + (w.woptind + 1), + argc - (w.woptind + 1), bind_mode, sets_bind_mode, + use_terminfo, streams)) { res = STATUS_BUILTIN_ERROR; } break; } - } break; } - - case BIND_KEY_NAMES: - { + case BIND_KEY_NAMES: { builtin_bind_key_names(all, streams); break; } - - - case BIND_FUNCTION_NAMES: - { + case BIND_FUNCTION_NAMES: { builtin_bind_function_names(streams); break; } - - - default: - { + default: { res = STATUS_BUILTIN_ERROR; streams.err.append_format(_(L"%ls: Invalid state\n"), argv[0]); break; @@ -793,236 +594,156 @@ static int builtin_bind(parser_t &parser, io_streams_t &streams, wchar_t **argv) return res; } - -/** - The block builtin, used for temporarily blocking events -*/ -static int builtin_block(parser_t &parser, io_streams_t &streams, wchar_t **argv) -{ +// The block builtin, used for temporarily blocking events. +static int builtin_block(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wgetopter_t w; - enum - { + enum { UNSET, GLOBAL, LOCAL, - } - ; + }; - int scope=UNSET; + int scope = UNSET; int erase = 0; - int argc=builtin_count_args(argv); + int argc = builtin_count_args(argv); - w.woptind=0; + w.woptind = 0; - static const struct woption - long_options[] = - { - { - L"erase", no_argument, 0, 'e' - } - , - { - L"local", no_argument, 0, 'l' - } - , - { - L"global", no_argument, 0, 'g' - } - , - { - L"help", no_argument, 0, 'h' - } - , - { - 0, 0, 0, 0 - } - } - ; + static const struct woption long_options[] = {{L"erase", no_argument, 0, 'e'}, + {L"local", no_argument, 0, 'l'}, + {L"global", no_argument, 0, 'g'}, + {L"help", no_argument, 0, 'h'}, + {0, 0, 0, 0}}; - while (1) - { + while (1) { int opt_index = 0; + int opt = w.wgetopt_long(argc, argv, L"elgh", long_options, &opt_index); + if (opt == -1) break; - int opt = w.wgetopt_long(argc, - argv, - L"elgh", - long_options, - &opt_index); - if (opt == -1) - break; - - switch (opt) - { - case 0: - if (long_options[opt_index].flag != 0) - break; - streams.err.append_format(BUILTIN_ERR_UNKNOWN, - argv[0], - long_options[opt_index].name); + switch (opt) { + case 0: { + if (long_options[opt_index].flag != 0) break; + streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0], + long_options[opt_index].name); builtin_print_help(parser, streams, argv[0], streams.err); - return STATUS_BUILTIN_ERROR; - case 'h': + } + case 'h': { builtin_print_help(parser, streams, argv[0], streams.out); return STATUS_BUILTIN_OK; - - case 'g': + } + case 'g': { scope = GLOBAL; break; - - case 'l': + } + case 'l': { scope = LOCAL; break; - - case 'e': + } + case 'e': { erase = 1; break; - - case '?': - builtin_unknown_option(parser, streams, argv[0], argv[w.woptind-1]); + } + case '?': { + builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]); return STATUS_BUILTIN_ERROR; - + } } - } - if (erase) - { - if (scope != UNSET) - { - streams.err.append_format(_(L"%ls: Can not specify scope when removing block\n"), argv[0]); + if (erase) { + if (scope != UNSET) { + streams.err.append_format(_(L"%ls: Can not specify scope when removing block\n"), + argv[0]); return STATUS_BUILTIN_ERROR; } - if (parser.global_event_blocks.empty()) - { + if (parser.global_event_blocks.empty()) { streams.err.append_format(_(L"%ls: No blocks defined\n"), argv[0]); return STATUS_BUILTIN_ERROR; } parser.global_event_blocks.pop_front(); - } - else - { + } else { size_t block_idx = 0; block_t *block = parser.block_at_index(block_idx); event_blockage_t eb = {}; - eb.typemask = (1<= parser.block_count()) - { + if (block_idx + 1 >= parser.block_count()) { block = NULL; } break; } - case GLOBAL: - { - block=NULL; + case GLOBAL: { + block = NULL; } - case UNSET: - { - while (block != NULL && block->type() != FUNCTION_CALL && block->type() != FUNCTION_CALL_NO_SHADOW) - { + case UNSET: { + while (block != NULL && block->type() != FUNCTION_CALL && + block->type() != FUNCTION_CALL_NO_SHADOW) { // Set it in function scope block = parser.block_at_index(++block_idx); } } } - if (block) - { + if (block) { block->event_blocks.push_front(eb); - } - else - { + } else { parser.global_event_blocks.push_front(eb); } } return STATUS_BUILTIN_OK; - } -/** - The builtin builtin, used for giving builtins precedence over - functions. Mostly handled by the parser. All this code does is some - additional operational modes, such as printing a list of all - builtins, printing help, etc. -*/ -static int builtin_builtin(parser_t &parser, io_streams_t &streams, wchar_t **argv) -{ - int argc=builtin_count_args(argv); - int list=0; +// The builtin builtin, used for giving builtins precedence over functions. Mostly handled by the +// parser. All this code does is some additional operational modes, such as printing a list of all +// builtins, printing help, etc. +static int builtin_builtin(parser_t &parser, io_streams_t &streams, wchar_t **argv) { + int argc = builtin_count_args(argv); + int list = 0; wgetopter_t w; - 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) - { + while (1) { int opt_index = 0; - int opt = w.wgetopt_long(argc, - argv, - L"nh", - long_options, - &opt_index); - if (opt == -1) - break; + int opt = w.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; - streams.err.append_format(BUILTIN_ERR_UNKNOWN, - argv[0], - long_options[opt_index].name); + switch (opt) { + case 0: { + if (long_options[opt_index].flag != 0) break; + streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0], + long_options[opt_index].name); builtin_print_help(parser, streams, argv[0], streams.err); - - return STATUS_BUILTIN_ERROR; - case 'h': + } + case 'h': { builtin_print_help(parser, streams, argv[0], streams.out); return STATUS_BUILTIN_OK; - - case 'n': - list=1; + } + case 'n': { + list = 1; break; - - case '?': - builtin_unknown_option(parser, streams, argv[0], argv[w.woptind-1]); + } + case '?': { + builtin_unknown_option(parser, streams, argv[0], argv[w.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); 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. */ + // 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(escape_string(name, true)); } - if (! desc.empty()) - { + if (!desc.empty()) { wcstring esc_desc = escape_string(desc, true); out.append(L" --description "); out.append(esc_desc); } - if (!function_get_shadows(name)) - { + if (!function_get_shadows(name)) { out.append(L" --no-scope-shadowing"); } - for (size_t i=0; itype) - { - case EVENT_SIGNAL: - { + switch (next->type) { + case EVENT_SIGNAL: { append_format(out, L" --on-signal %ls", sig2wcs(next->param1.signal)); break; } - - case EVENT_VARIABLE: - { + case EVENT_VARIABLE: { append_format(out, L" --on-variable %ls", next->str_param1.c_str()); break; } - - case EVENT_EXIT: - { + 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: - { + 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); + if (j) append_format(out, L" --on-job-exit %d", j->pgid); break; } - - case EVENT_GENERIC: - { + 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.empty()) - { + if (!named.empty()) { append_format(out, L" --argument"); - for (size_t i=0; i < named.size(); i++) - { + 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) - { + // Output the function name if we deferred it. + if (defer_function_name) { out.append(L" -- "); out.append(escape_string(name, true)); } - /* Output any inherited variables as `set -l` lines */ - std::map inherit_vars = function_get_inherit_vars(name); - for (std::map::const_iterator it = inherit_vars.begin(), end = inherit_vars.end(); it != end; ++it) - { + // Output any inherited variables as `set -l` lines. + std::map inherit_vars = function_get_inherit_vars(name); + for (std::map::const_iterator it = inherit_vars.begin(), + end = inherit_vars.end(); + it != end; ++it) { wcstring_list_t lst; - if (!it->second.missing()) - { + if (!it->second.missing()) { tokenize_variable_array(it->second, lst); } - /* This forced tab is crummy, but we don't know what indentation style the function uses */ + // This forced tab is crummy, but we don't know what indentation style the function uses. append_format(out, L"\n\tset -l %ls", it->first.c_str()); - for (wcstring_list_t::const_iterator arg_it = lst.begin(), arg_end = lst.end(); arg_it != arg_end; ++arg_it) - { + for (wcstring_list_t::const_iterator arg_it = lst.begin(), arg_end = lst.end(); + arg_it != arg_end; ++arg_it) { wcstring earg = escape_string(*arg_it, ESCAPE_ALL); out.push_back(L' '); out.append(earg); } } - /* This forced tab is sort of crummy - not all functions start with a tab */ + // This forced tab is sort of crummy - not all functions start with a tab. append_format(out, L"\n\t%ls", def.c_str()); - /* Append a newline before the 'end', unless there already is one there */ - if (! string_suffixes_string(L"\n", def)) - { + // Append a newline before the 'end', unless there already is one there. + if (!string_suffixes_string(L"\n", def)) { out.push_back(L'\n'); } out.append(L"end\n"); return out; } - -/** - The functions builtin, used for listing and erasing functions. -*/ -static int builtin_functions(parser_t &parser, io_streams_t &streams, wchar_t **argv) -{ +// The functions builtin, used for listing and erasing functions. +static int builtin_functions(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wgetopter_t w; int i; - int erase=0; - wchar_t *desc=0; + int erase = 0; + wchar_t *desc = 0; - int argc=builtin_count_args(argv); - int list=0; - int show_hidden=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; - 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) - { + while (1) { int opt_index = 0; - int opt = w.wgetopt_long(argc, - argv, - L"ed:nahqc", - long_options, - &opt_index); - if (opt == -1) - break; + int opt = w.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; - streams.err.append_format(BUILTIN_ERR_UNKNOWN, - argv[0], - long_options[opt_index].name); + switch (opt) { + case 0: { + if (long_options[opt_index].flag != 0) break; + streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0], + long_options[opt_index].name); builtin_print_help(parser, streams, argv[0], streams.err); - - return STATUS_BUILTIN_ERROR; - - case 'e': - erase=1; + } + case 'e': { + erase = 1; break; - - case 'd': - desc=w.woptarg; + } + case 'd': { + desc = w.woptarg; break; - - case 'n': - list=1; + } + case 'n': { + list = 1; break; - - case 'a': - show_hidden=1; + } + case 'a': { + show_hidden = 1; break; - - case 'h': + } + case 'h': { builtin_print_help(parser, streams, argv[0], streams.out); return STATUS_BUILTIN_OK; - - case 'q': + } + case 'q': { query = 1; break; - - case 'c': + } + case 'c': { copy = 1; break; - - case '?': - builtin_unknown_option(parser, streams, argv[0], argv[w.woptind-1]); + } + case '?': { + builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]); return STATUS_BUILTIN_ERROR; - + } } - } - /* - Erase, desc, query, copy and list are mutually exclusive - */ - if ((erase + (!!desc) + list + query + copy) > 1) - { - streams.err.append_format(_(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) { + streams.err.append_format(_(L"%ls: Invalid combination of options\n"), argv[0]); builtin_print_help(parser, streams, argv[0], streams.err); return STATUS_BUILTIN_ERROR; } - if (erase) - { + if (erase) { int i; - for (i=w.woptind; i start) - { + // We succeeded if we consumed at least one digit. + if (idx > start) { *consumed = idx; *out_val = val; success = true; @@ -1749,140 +1299,137 @@ static bool builtin_echo_parse_numeric_sequence(const wchar_t *str, size_t *cons return success; } -/** The echo builtin. - bash only respects -n if it's the first argument. We'll do the same. - We also support a new option -s to mean "no spaces" -*/ - -static int builtin_echo(parser_t &parser, io_streams_t &streams, wchar_t **argv) -{ +// The echo builtin. +// +// Bash only respects -n if it's the first argument. We'll do the same. We also support a new option +// -s to mean "no spaces" +static int builtin_echo(parser_t &parser, io_streams_t &streams, wchar_t **argv) { /* Skip first arg */ - if (! *argv++) - return STATUS_BUILTIN_ERROR; + if (!*argv++) return STATUS_BUILTIN_ERROR; - /* Process options. Options must come at the beginning - the first non-option kicks us out. */ + // Process options. Options must come at the beginning - the first non-option kicks us out. bool print_newline = true, print_spaces = true, interpret_special_chars = false; size_t option_idx = 0; - for (option_idx = 0; argv[option_idx] != NULL; option_idx++) - { + for (option_idx = 0; argv[option_idx] != NULL; option_idx++) { const wchar_t *arg = argv[option_idx]; assert(arg != NULL); bool arg_is_valid_option = false; - if (arg[0] == L'-') - { + if (arg[0] == L'-') { // We have a leading dash. Ensure that every subseqnent character is a valid option. size_t i = 1; - while (arg[i] != L'\0' && wcschr(L"nesE", arg[i]) != NULL) - { + while (arg[i] != L'\0' && wcschr(L"nesE", arg[i]) != NULL) { i++; } - // We must have at least two characters to be a valid option, and have consumed the whole string + // We must have at least two characters to be a valid option, and have consumed the + // whole string. arg_is_valid_option = (i >= 2 && arg[i] == L'\0'); } - - if (! arg_is_valid_option) - { - // This argument is not an option, so there are no more options + + if (!arg_is_valid_option) { + // This argument is not an option, so there are no more options. break; } - + // Ok, we are sure the argument is an option. Parse it. assert(arg_is_valid_option); - for (size_t i=1; arg[i] != L'\0'; i++) - { - switch (arg[i]) - { - case L'n': + for (size_t i = 1; arg[i] != L'\0'; i++) { + switch (arg[i]) { + case L'n': { print_newline = false; break; - case L'e': + } + case L'e': { interpret_special_chars = true; break; - case L's': + } + case L's': { print_spaces = false; break; - case L'E': + } + case L'E': { interpret_special_chars = false; break; - default: + } + default: { assert(0 && "Unexpected character in builtin_echo argument"); break; + } } } } - /* The special character \c can be used to indicate no more output */ + // The special character \c can be used to indicate no more output. bool continue_output = true; - + /* Skip over the options */ - const wchar_t * const *args_to_echo = argv + option_idx; - for (size_t idx = 0; continue_output && args_to_echo[idx] != NULL; idx++) - { - if (print_spaces && idx > 0) - { + const wchar_t *const *args_to_echo = argv + option_idx; + for (size_t idx = 0; continue_output && args_to_echo[idx] != NULL; idx++) { + if (print_spaces && idx > 0) { streams.out.push_back(' '); } const wchar_t *str = args_to_echo[idx]; - for (size_t j=0; continue_output && str[j]; j++) - { - if (! interpret_special_chars || str[j] != L'\\') - { - /* Not an escape */ + for (size_t j = 0; continue_output && str[j]; j++) { + if (!interpret_special_chars || str[j] != L'\\') { + // Not an escape. streams.out.push_back(str[j]); - } - else - { - /* Most escapes consume one character in addition to the backslash; the numeric sequences may consume more, while an unrecognized escape sequence consumes none. */ + } else { + // Most escapes consume one character in addition to the backslash; the numeric + // sequences may consume more, while an unrecognized escape sequence consumes none. wchar_t wc; size_t consumed = 1; - switch (str[j+1]) - { - case L'a': + switch (str[j + 1]) { + case L'a': { wc = L'\a'; break; - case L'b': + } + case L'b': { wc = L'\b'; break; - case L'e': + } + case L'e': { wc = L'\x1B'; break; - case L'f': + } + case L'f': { wc = L'\f'; break; - case L'n': + } + case L'n': { wc = L'\n'; break; - case L'r': + } + case L'r': { wc = L'\r'; break; - case L't': + } + case L't': { wc = L'\t'; break; - case L'v': + } + case L'v': { wc = L'\v'; break; - case L'\\': + } + case L'\\': { wc = L'\\'; break; - - case L'c': + } + case L'c': { wc = 0; continue_output = false; break; - - default: - { - /* Octal and hex escape sequences */ + } + default: { + // 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. The narrow_val is a literal byte that we want to output (#1894) */ + if (builtin_echo_parse_numeric_sequence(str + j + 1, &consumed, + &narrow_val)) { + // Here consumed must have been set to something. The narrow_val is a + // literal byte that we want to output (#1894). wc = ENCODE_DIRECT_BASE + narrow_val % 256; - } - else - { - /* Not a recognized escape. We consume only the backslash. */ + } else { + // Not a recognized escape. We consume only the backslash. wc = L'\\'; consumed = 0; } @@ -1890,18 +1437,17 @@ static int builtin_echo(parser_t &parser, io_streams_t &streams, wchar_t **argv) } } - /* Skip over characters that were part of this escape sequence (but not the backslash, which will be handled by the loop increment */ + // 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) - { + if (continue_output) { streams.out.push_back(wc); } } } } - if (print_newline && continue_output) - { + if (print_newline && continue_output) { streams.out.push_back('\n'); } return STATUS_BUILTIN_OK; @@ -1909,41 +1455,38 @@ static int builtin_echo(parser_t &parser, io_streams_t &streams, 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, io_streams_t &streams, wchar_t **argv) -{ +static int builtin_pwd(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wcstring res = wgetcwd(); - if (res.empty()) - { + if (res.empty()) { return STATUS_BUILTIN_ERROR; - } - else - { + } else { streams.out.append(res); streams.out.push_back(L'\n'); return STATUS_BUILTIN_OK; } } -/** Adds a function to the function set. It calls into function.cpp to perform any heavy lifting. */ -int define_function(parser_t &parser, io_streams_t &streams, const wcstring_list_t &c_args, const wcstring &contents, int definition_line_offset, wcstring *out_err) -{ +// Adds a function to the function set. It calls into function.cpp to perform any heavy lifting. +int define_function(parser_t &parser, io_streams_t &streams, const wcstring_list_t &c_args, + const wcstring &contents, int definition_line_offset, wcstring *out_err) { wgetopter_t w; assert(out_err != NULL); - /* wgetopt expects 'function' as the first argument. Make a new wcstring_list with that property. */ + // wgetopt expects 'function' as the first argument. Make a new wcstring_list with that + // property. wcstring_list_t args; args.push_back(L"function"); args.insert(args.end(), c_args.begin(), c_args.end()); - /* Hackish const_cast matches the one in builtin_run */ + // Hackish const_cast matches the one in builtin_run. const null_terminated_array_t argv_array(args); wchar_t **argv = const_cast(argv_array.get()); int argc = builtin_count_args(argv); - int res=STATUS_BUILTIN_OK; - wchar_t *desc=0; + int res = STATUS_BUILTIN_OK; + wchar_t *desc = 0; std::vector events; - + bool has_named_arguments = false; wcstring_list_t named_arguments; wcstring_list_t inherit_vars; @@ -1951,197 +1494,142 @@ int define_function(parser_t &parser, io_streams_t &streams, const wcstring_list bool shadows = true; wcstring_list_t wrap_targets; - - /* If -a/--argument-names is specified before the function name, - then the function name is the last positional, e.g. `function -a arg1 arg2 name`. - If it is specified after the function name (or not specified at all) then the - function name is the first positional. This is the common case. */ + + // If -a/--argument-names is specified before the function name, then the function name is the + // last positional, e.g. `function -a arg1 arg2 name`. If it is specified after the function + // name (or not specified at all) then the function name is the first positional. This is the + // common case. bool name_is_first_positional = true; wcstring_list_t positionals; - - 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"wraps", required_argument, 0, 'w' }, - { L"help", no_argument, 0, 'h' }, - { L"argument-names", no_argument, 0, 'a' }, - { L"no-scope-shadowing", no_argument, 0, 'S' }, - { L"inherit-variable", required_argument, 0, 'V' }, - { 0, 0, 0, 0 } - }; - while (1 && (!res)) - { + 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"wraps", required_argument, 0, 'w'}, + {L"help", no_argument, 0, 'h'}, + {L"argument-names", no_argument, 0, 'a'}, + {L"no-scope-shadowing", no_argument, 0, 'S'}, + {L"inherit-variable", required_argument, 0, 'V'}, + {0, 0, 0, 0}}; + + while (1 && (!res)) { int opt_index = 0; - // The leading - here specifies RETURN_IN_ORDER - int opt = w.wgetopt_long(argc, - argv, - L"-d:s:j:p:v:e:w:haSV:", - long_options, - &opt_index); - if (opt == -1) - break; - - switch (opt) - { - case 0: - if (long_options[opt_index].flag != 0) - break; - - - - append_format(*out_err, - BUILTIN_ERR_UNKNOWN, - argv[0], - long_options[opt_index].name); + // The leading - here specifies RETURN_IN_ORDER. + int opt = w.wgetopt_long(argc, argv, L"-d:s:j:p:v:e:w:haSV:", long_options, &opt_index); + if (opt == -1) break; + switch (opt) { + case 0: { + if (long_options[opt_index].flag != 0) break; + append_format(*out_err, BUILTIN_ERR_UNKNOWN, argv[0], long_options[opt_index].name); res = 1; break; - - case 'd': - desc=w.woptarg; + } + case 'd': { + desc = w.woptarg; break; - - case 's': - { + } + case 's': { int sig = wcs2sig(w.woptarg); - - if (sig < 0) - { - append_format(*out_err, - _(L"%ls: Unknown signal '%ls'"), - argv[0], - w.woptarg); - res=1; + if (sig < 0) { + append_format(*out_err, _(L"%ls: Unknown signal '%ls'"), argv[0], w.woptarg); + res = 1; break; } events.push_back(event_t::signal_event(sig)); break; } - case 'v': - { - if (wcsvarname(w.woptarg)) - { - append_format(*out_err, - _(L"%ls: Invalid variable name '%ls'"), - argv[0], + case 'v': { + if (wcsvarname(w.woptarg)) { + append_format(*out_err, _(L"%ls: Invalid variable name '%ls'"), argv[0], w.woptarg); - res=STATUS_BUILTIN_ERROR; + res = STATUS_BUILTIN_ERROR; break; } events.push_back(event_t::variable_event(w.woptarg)); break; } - - - case 'e': - { + case 'e': { events.push_back(event_t::generic_event(w.woptarg)); break; } - case 'j': - case 'p': - { + case 'p': { pid_t pid; wchar_t *end; event_t e(EVENT_ANY); - if ((opt == 'j') && - (wcscasecmp(w.woptarg, L"caller") == 0)) - { + if ((opt == 'j') && (wcscasecmp(w.woptarg, L"caller") == 0)) { int job_id = -1; - if (is_subshell) - { + if (is_subshell) { size_t block_idx = 0; - /* Find the outermost substitution block */ - for (block_idx = 0; ; block_idx++) - { + // Find the outermost substitution block. + for (block_idx = 0;; block_idx++) { const block_t *b = parser.block_at_index(block_idx); - if (b == NULL || b->type() == SUBST) - break; + if (b == NULL || b->type() == SUBST) break; } - /* Go one step beyond that, to get to the caller */ + // Go one step beyond that, to get to the caller. const block_t *caller_block = parser.block_at_index(block_idx + 1); - if (caller_block != NULL && caller_block->job != NULL) - { + if (caller_block != NULL && caller_block->job != NULL) { job_id = caller_block->job->job_id; } } - if (job_id == -1) - { + if (job_id == -1) { append_format(*out_err, _(L"%ls: Cannot find calling job for event handler"), argv[0]); - res=1; - } - else - { + res = 1; + } else { e.type = EVENT_JOB_ID; e.param1.job_id = job_id; } - } - else - { + } else { errno = 0; pid = fish_wcstoi(w.woptarg, &end, 10); - if (errno || !end || *end) - { - append_format(*out_err, - _(L"%ls: Invalid process id %ls"), - argv[0], + if (errno || !end || *end) { + append_format(*out_err, _(L"%ls: Invalid process id %ls"), argv[0], w.woptarg); - res=1; + res = 1; break; } - e.type = EVENT_EXIT; - e.param1.pid = (opt=='j'?-1:1)*abs(pid); + e.param1.pid = (opt == 'j' ? -1 : 1) * abs(pid); } - if (res) - { - /* nothing */ - } - else - { + if (!res) { events.push_back(e); } break; } - - case 'a': + case 'a': { has_named_arguments = true; - /* The function name is the first positional unless -a comes before all positionals */ - name_is_first_positional = ! positionals.empty(); + // The function name is the first positional unless -a comes before all positionals. + name_is_first_positional = !positionals.empty(); break; - - case 'S': + } + case 'S': { shadows = 0; break; - - case 'w': + } + case 'w': { wrap_targets.push_back(w.woptarg); break; - - case 'V': - { - if (wcsvarname(w.woptarg)) - { - append_format(*out_err, _(L"%ls: Invalid variable name '%ls'"), argv[0], w.woptarg); + } + case 'V': { + if (wcsvarname(w.woptarg)) { + append_format(*out_err, _(L"%ls: Invalid variable name '%ls'"), argv[0], + w.woptarg); res = STATUS_BUILTIN_ERROR; break; } @@ -2149,120 +1637,87 @@ int define_function(parser_t &parser, io_streams_t &streams, const wcstring_list inherit_vars.push_back(w.woptarg); break; } - - case 'h': + case 'h': { builtin_print_help(parser, streams, argv[0], streams.out); return STATUS_BUILTIN_OK; - - case 1: + } + case 1: { assert(w.woptarg != NULL); positionals.push_back(w.woptarg); break; - - case '?': - builtin_unknown_option(parser, streams, argv[0], argv[w.woptind-1]); + } + case '?': { + builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]); res = 1; break; - + } } - } - if (!res) - { - /* Determine the function name, and remove it from the list of positionals */ + if (!res) { + // Determine the function name, and remove it from the list of positionals. wcstring function_name; bool name_is_missing = positionals.empty(); - if (! name_is_missing) - { - if (name_is_first_positional) - { + if (!name_is_missing) { + if (name_is_first_positional) { function_name = positionals.front(); positionals.erase(positionals.begin()); - } - else - { + } else { function_name = positionals.back(); positionals.erase(positionals.end() - 1); } } - - if (name_is_missing) - { - append_format(*out_err, - _(L"%ls: Expected function name"), - argv[0]); - res=1; - } - else if (wcsfuncname(function_name)) - { - append_format(*out_err, - _(L"%ls: Illegal function name '%ls'"), - argv[0], + + if (name_is_missing) { + append_format(*out_err, _(L"%ls: Expected function name"), argv[0]); + res = 1; + } else if (wcsfuncname(function_name)) { + append_format(*out_err, _(L"%ls: Illegal function name '%ls'"), argv[0], function_name.c_str()); - res=1; - } - else if (parser_keywords_is_reserved(function_name)) - { + res = 1; + } else if (parser_keywords_is_reserved(function_name)) { + append_format( + *out_err, + _(L"%ls: The name '%ls' is reserved,\nand can not be used as a function name"), + argv[0], function_name.c_str()); - append_format(*out_err, - _(L"%ls: The name '%ls' is reserved,\nand can not be used as a function name"), - argv[0], - function_name.c_str()); - - res=1; - } - else if (function_name.empty()) - { + res = 1; + } else if (function_name.empty()) { append_format(*out_err, _(L"%ls: No function name given"), argv[0]); - res=1; - } - else - { - if (has_named_arguments) - { - /* All remaining positionals are named arguments */ + res = 1; + } else { + if (has_named_arguments) { + // All remaining positionals are named arguments. named_arguments.swap(positionals); - for (size_t i=0; i < named_arguments.size(); i++) - { - if (wcsvarname(named_arguments.at(i))) - { - append_format(*out_err, - _(L"%ls: Invalid variable name '%ls'"), - argv[0], + for (size_t i = 0; i < named_arguments.size(); i++) { + if (wcsvarname(named_arguments.at(i))) { + append_format(*out_err, _(L"%ls: Invalid variable name '%ls'"), argv[0], named_arguments.at(i).c_str()); res = STATUS_BUILTIN_ERROR; break; } } - } - else if (! positionals.empty()) - { - // +1 because we already got the function name - append_format(*out_err, - _(L"%ls: Expected one argument, got %lu"), - argv[0], + } else if (!positionals.empty()) { + // +1 because we already got the function name. + append_format(*out_err, _(L"%ls: Expected one argument, got %lu"), argv[0], (unsigned long)(positionals.size() + 1)); - res=1; + res = 1; } } - if (!res) - { - /* Here we actually define the function! */ + if (!res) { + // Here we actually define the function! function_data_t d; d.name = function_name; - if (desc) - d.description = desc; + if (desc) d.description = desc; d.events.swap(events); d.shadows = shadows; d.named_arguments.swap(named_arguments); d.inherit_vars.swap(inherit_vars); - for (size_t i=0; i 1) - { - streams.err.append_format(BUILTIN_ERR_GLOCAL, - argv[0]); + if ((place & ENV_LOCAL ? 1 : 0) + (place & ENV_GLOBAL ? 1 : 0) + + (place & ENV_UNIVERSAL ? 1 : 0) > + 1) { + streams.err.append_format(BUILTIN_ERR_GLOCAL, argv[0]); builtin_print_help(parser, streams, argv[0], streams.err); return STATUS_BUILTIN_ERROR; } - if (array && w.woptind+1 != argc) - { - streams.err.append_format(_(L"%ls: --array option requires a single variable name.\n"), argv[0]); + if (array && w.woptind + 1 != argc) { + streams.err.append_format(_(L"%ls: --array option requires a single variable name.\n"), + argv[0]); builtin_print_help(parser, streams, argv[0], streams.err); return STATUS_BUILTIN_ERROR; } - /* - Verify all variable names - */ - for (i=w.woptind; i 0) - { - wcstring chars(bufflen+(bufflen-1), ARRAY_SEP); + if (array) { + if (bufflen > 0) { + wcstring chars(bufflen + (bufflen - 1), ARRAY_SEP); wcstring::iterator out = chars.begin(); - for (wcstring::const_iterator it = buff.begin(), end = buff.end(); it != end; ++it) - { + for (wcstring::const_iterator it = buff.begin(), end = buff.end(); it != end; + ++it) { *out = *it; out += 2; } env_set(argv[i], chars.c_str(), place); - } - else - { + } else { env_set(argv[i], NULL, place); } - } - else - { + } else { size_t j = 0; - for (; i+1 < argc; ++i) - { - if (j < bufflen) - { + for (; i + 1 < argc; ++i) { + if (j < bufflen) { wchar_t buffer[2] = {buff[j++], 0}; env_set(argv[i], buffer, place); - } - else - { + } else { env_set(argv[i], L"", place); } } if (i < argc) env_set(argv[i], &buff[j], place); } - } - else if (array) - { + } else if (array) { wcstring tokens; tokens.reserve(buff.size()); bool empty = true; - - for (wcstring_range loc = wcstring_tok(buff, ifs); loc.first != wcstring::npos; loc = wcstring_tok(buff, ifs, loc)) - { + + for (wcstring_range loc = wcstring_tok(buff, ifs); loc.first != wcstring::npos; + loc = wcstring_tok(buff, ifs, loc)) { if (!empty) tokens.push_back(ARRAY_SEP); tokens.append(buff, loc.first, loc.second); empty = false; } env_set(argv[i], empty ? NULL : tokens.c_str(), place); - } - else - { - wcstring_range loc = wcstring_range(0,0); + } else { + wcstring_range loc = wcstring_range(0, 0); - while (i= 0 && (size_t)opt_index < sizeof long_options / sizeof *long_options); - if (long_options[opt_index].flag != 0) - break; - streams.err.append_format(BUILTIN_ERR_UNKNOWN, - argv[0], - long_options[opt_index].name); + 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; + streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0], + long_options[opt_index].name); builtin_print_help(parser, streams, argv[0], streams.err); return STATUS_BUILTIN_ERROR; - - - case 'h': + } + case 'h': { builtin_print_help(parser, streams, argv[0], streams.out); return STATUS_BUILTIN_OK; - - - case ':': - builtin_missing_argument(parser, streams, argv[0], argv[w.woptind-1]); + } + case ':': { + builtin_missing_argument(parser, streams, argv[0], argv[w.woptind - 1]); return STATUS_BUILTIN_ERROR; - - case '?': - builtin_unknown_option(parser, streams, argv[0], argv[w.woptind-1]); + } + case '?': { + builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]); return STATUS_BUILTIN_ERROR; - - case 'i': + } + case 'i': { should_output_index = true; break; + } } - } needle = argv[w.woptind]; - if (!needle) - { + if (!needle) { streams.err.append_format(_(L"%ls: Key not specified\n"), argv[0]); - } - else - { - for (int i=w.woptind+1; i" : fn_intern); - } - else - { + if (res) { + streams.err.append_format(_(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. - */ - + // Do not close fd after calling reader_read. reader_read automatically closes it before calling + // eval. reader_pop_current_filename(); return res; } -/** - Make the specified job the first job of the job list. Moving jobs - around in the list makes the list reflect the order in which the - jobs were used. -*/ -static void make_first(job_t *j) -{ - job_promote(j); -} +// Make the specified job the first job of the job list. Moving jobs around in the list makes the +// list reflect the order in which the jobs were used. +static void make_first(job_t *j) { job_promote(j); } +// Builtin for putting a job in the foreground. +static int builtin_fg(parser_t &parser, io_streams_t &streams, wchar_t **argv) { + job_t *j = NULL; -/** - Builtin for putting a job in the foreground -*/ -static int builtin_fg(parser_t &parser, io_streams_t &streams, wchar_t **argv) -{ - 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())) - { + 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))) - { + ((job_is_stopped(j) || (!job_get_flag(j, JOB_FOREGROUND))) && + job_get_flag(j, JOB_CONTROL))) { break; } } - if (!j) - { - streams.err.append_format(_(L"%ls: There are no suitable jobs\n"), - argv[0]); + if (!j) { + streams.err.append_format(_(L"%ls: There are no suitable jobs\n"), argv[0]); } - } - 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 - */ + } 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)) - { + if (!(*endptr || errno)) { j = job_get_from_pid(pid); - if (j) - found_job = 1; + if (j) found_job = 1; } - if (found_job) - { - streams.err.append_format(_(L"%ls: Ambiguous job\n"), - argv[0]); - } - else - { - streams.err.append_format(_(L"%ls: '%ls' is not a job\n"), - argv[0], - argv[1]); + if (found_job) { + streams.err.append_format(_(L"%ls: Ambiguous job\n"), argv[0]); + } else { + streams.err.append_format(_(L"%ls: '%ls' is not a job\n"), argv[0], argv[1]); } builtin_print_help(parser, streams, argv[0], streams.err); - j=0; + j = 0; - } - else - { + } else { wchar_t *end; int pid; errno = 0; pid = abs(fish_wcstoi(argv[1], &end, 10)); - if (*end || errno) - { - streams.err.append_format(BUILTIN_ERR_NOT_NUMBER, - argv[0], - argv[1]); + if (*end || errno) { + streams.err.append_format(BUILTIN_ERR_NOT_NUMBER, argv[0], argv[1]); builtin_print_help(parser, streams, argv[0], streams.err); - } - else - { + } else { j = job_get_from_pid(pid); - if (!j || !job_get_flag(j, JOB_CONSTRUCTED) || job_is_completed(j)) - { - streams.err.append_format(_(L"%ls: No suitable job: %d\n"), - argv[0], - pid); + if (!j || !job_get_flag(j, JOB_CONSTRUCTED) || job_is_completed(j)) { + streams.err.append_format(_(L"%ls: No suitable job: %d\n"), argv[0], pid); builtin_print_help(parser, streams, argv[0], streams.err); - j=0; - } - else if (!job_get_flag(j, JOB_CONTROL)) - { - streams.err.append_format(_(L"%ls: Can't put job %d, '%ls' to foreground because it is not under job control\n"), - argv[0], - pid, - j->command_wcstr()); + j = 0; + } else if (!job_get_flag(j, JOB_CONTROL)) { + streams.err.append_format(_(L"%ls: Can't put job %d, '%ls' to foreground because " + L"it is not under job control\n"), + argv[0], pid, j->command_wcstr()); builtin_print_help(parser, streams, argv[0], streams.err); - j=0; + j = 0; } } } - if (j) - { - if (streams.err_is_redirected) - { - streams.err.append_format(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()); + if (j) { + if (streams.err_is_redirected) { + streams.err.append_format(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()); } const wcstring ft = tok_first(j->command()); - if (! ft.empty()) - env_set(L"_", ft.c_str(), ENV_EXPORT); + if (!ft.empty()) env_set(L"_", ft.c_str(), ENV_EXPORT); reader_write_title(j->command()); make_first(j); @@ -3551,33 +2632,21 @@ static int builtin_fg(parser_t &parser, io_streams_t &streams, wchar_t **argv) return j != 0; } -/** - Helper function for builtin_bg() -*/ -static int send_to_bg(parser_t &parser, io_streams_t &streams, job_t *j, const wchar_t *name) -{ - if (j == 0) - { - streams.err.append_format(_(L"%ls: Unknown job '%ls'\n"), - L"bg", - name); +// Helper function for builtin_bg(). +static int send_to_bg(parser_t &parser, io_streams_t &streams, job_t *j, const wchar_t *name) { + if (j == 0) { + streams.err.append_format(_(L"%ls: Unknown job '%ls'\n"), L"bg", name); builtin_print_help(parser, streams, L"bg", streams.err); return STATUS_BUILTIN_ERROR; - } - else if (!job_get_flag(j, JOB_CONTROL)) - { - streams.err.append_format(_(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()); + } else if (!job_get_flag(j, JOB_CONTROL)) { + streams.err.append_format( + _(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, streams, L"bg", streams.err); return STATUS_BUILTIN_ERROR; - } - else - { - streams.err.append_format(_(L"Send job %d '%ls' to background\n"), - j->job_id, - j->command_wcstr()); + } else { + streams.err.append_format(_(L"Send job %d '%ls' to background\n"), j->job_id, + j->command_wcstr()); } make_first(j); job_set_flag(j, JOB_FOREGROUND, 0); @@ -3585,62 +2654,43 @@ static int send_to_bg(parser_t &parser, io_streams_t &streams, job_t *j, const w return STATUS_BUILTIN_OK; } - -/** - Builtin for putting a job in the background -*/ -static int builtin_bg(parser_t &parser, io_streams_t &streams, wchar_t **argv) -{ +// Builtin for putting a job in the background. +static int builtin_bg(parser_t &parser, io_streams_t &streams, wchar_t **argv) { int res = STATUS_BUILTIN_OK; - if (argv[1] == 0) - { + 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))) - { + while ((j = jobs.next())) { + if (job_is_stopped(j) && job_get_flag(j, JOB_CONTROL) && (!job_is_completed(j))) { break; } } - if (!j) - { - streams.err.append_format(_(L"%ls: There are no suitable jobs\n"), - argv[0]); + if (!j) { + streams.err.append_format(_(L"%ls: There are no suitable jobs\n"), argv[0]); res = 1; - } - else - { + } else { res = send_to_bg(parser, streams, j, _(L"(default)")); } - } - else - { + } else { wchar_t *end; int i; int pid; int err = 0; - for (i=1; argv[i]; i++) - { - errno=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)) - { - streams.err.append_format(_(L"%ls: '%ls' is not a job\n"), - argv[0], - argv[i]); + if (errno || pid < 0 || *end || !job_get_from_pid(pid)) { + streams.err.append_format(_(L"%ls: '%ls' is not a job\n"), argv[0], argv[i]); err = 1; break; } } - if (!err) - { - for (i=1; !res && argv[i]; i++) - { + if (!err) { + for (i = 1; !res && argv[i]; i++) { pid = fish_wcstoi(argv[i], 0, 10); res |= send_to_bg(parser, streams, job_get_from_pid(pid), *argv); } @@ -3650,48 +2700,36 @@ static int builtin_bg(parser_t &parser, io_streams_t &streams, wchar_t **argv) return res; } - -/** - This function handles both the 'continue' and the 'break' builtins - that are used for loop control. -*/ -static int builtin_break_continue(parser_t &parser, io_streams_t &streams, wchar_t **argv) -{ - int is_break = (wcscmp(argv[0],L"break")==0); +// This function handles both the 'continue' and the 'break' builtins that are used for loop +// control. +static int builtin_break_continue(parser_t &parser, io_streams_t &streams, wchar_t **argv) { + int is_break = (wcscmp(argv[0], L"break") == 0); int argc = builtin_count_args(argv); - - if (argc != 1) - { - streams.err.append_format(BUILTIN_ERR_UNKNOWN, - argv[0], - argv[1]); + if (argc != 1) { + streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0], argv[1]); builtin_print_help(parser, streams, argv[0], streams.err); return STATUS_BUILTIN_ERROR; } - /* Find the index of the enclosing for or while loop. Recall that incrementing loop_idx goes 'up' to outer blocks */ + // Find the index of the enclosing for or while loop. Recall that incrementing loop_idx goes + // 'up' to outer blocks. size_t loop_idx; - for (loop_idx = 0; loop_idx < parser.block_count(); loop_idx++) - { + for (loop_idx = 0; loop_idx < parser.block_count(); loop_idx++) { const block_t *b = parser.block_at_index(loop_idx); - if (b->type() == WHILE || b->type() == FOR) - break; + if (b->type() == WHILE || b->type() == FOR) break; } - if (loop_idx >= parser.block_count()) - { - streams.err.append_format(_(L"%ls: Not inside of loop\n"), - argv[0]); + if (loop_idx >= parser.block_count()) { + streams.err.append_format(_(L"%ls: Not inside of loop\n"), argv[0]); builtin_print_help(parser, streams, argv[0], streams.err); return STATUS_BUILTIN_ERROR; } - /* Skip blocks interior to the loop */ + // Skip blocks interior to the loop. size_t block_idx = loop_idx; - while (block_idx--) - { + while (block_idx--) { parser.block_at_index(block_idx)->skip = true; } @@ -3702,13 +2740,8 @@ static int builtin_break_continue(parser_t &parser, io_streams_t &streams, wchar return STATUS_BUILTIN_OK; } -/** - Implementation of the builtin breakpoint command, used to launch the - interactive debugger. - */ - -static int builtin_breakpoint(parser_t &parser, io_streams_t &streams, wchar_t **argv) -{ +// Implementation of the builtin breakpoint command, used to launch the interactive debugger. +static int builtin_breakpoint(parser_t &parser, io_streams_t &streams, wchar_t **argv) { parser.push_block(new breakpoint_block_t()); reader_read(STDIN_FILENO, streams.io_chain ? *streams.io_chain : io_chain_t()); @@ -3718,61 +2751,49 @@ static int builtin_breakpoint(parser_t &parser, io_streams_t &streams, wchar_t * return proc_get_last_status(); } - -/** - Function for handling the \c return builtin -*/ -static int builtin_return(parser_t &parser, io_streams_t &streams, wchar_t **argv) -{ +// Function for handling the \c return builtin. +static int builtin_return(parser_t &parser, io_streams_t &streams, wchar_t **argv) { int argc = builtin_count_args(argv); int status = proc_get_last_status(); - switch (argc) - { - case 1: + switch (argc) { + case 1: { break; - case 2: - { + } + case 2: { wchar_t *end; errno = 0; - status = fish_wcstoi(argv[1],&end,10); - if (errno || *end != 0) - { - streams.err.append_format(_(L"%ls: Argument '%ls' must be an integer\n"), - argv[0], - argv[1]); + status = fish_wcstoi(argv[1], &end, 10); + if (errno || *end != 0) { + streams.err.append_format(_(L"%ls: Argument '%ls' must be an integer\n"), argv[0], + argv[1]); builtin_print_help(parser, streams, argv[0], streams.err); return STATUS_BUILTIN_ERROR; } break; } - default: - streams.err.append_format(_(L"%ls: Too many arguments\n"), - argv[0]); + default: { + streams.err.append_format(_(L"%ls: Too many arguments\n"), argv[0]); builtin_print_help(parser, streams, argv[0], streams.err); return STATUS_BUILTIN_ERROR; + } } - /* Find the function block */ + // Find the function block. size_t function_block_idx; - for (function_block_idx = 0; function_block_idx < parser.block_count(); function_block_idx++) - { + for (function_block_idx = 0; function_block_idx < parser.block_count(); function_block_idx++) { const block_t *b = parser.block_at_index(function_block_idx); - if (b->type() == FUNCTION_CALL || b->type() == FUNCTION_CALL_NO_SHADOW) - break; + if (b->type() == FUNCTION_CALL || b->type() == FUNCTION_CALL_NO_SHADOW) break; } - if (function_block_idx >= parser.block_count()) - { - streams.err.append_format(_(L"%ls: Not inside of function\n"), - argv[0]); + if (function_block_idx >= parser.block_count()) { + streams.err.append_format(_(L"%ls: Not inside of function\n"), argv[0]); builtin_print_help(parser, streams, argv[0], streams.err); return STATUS_BUILTIN_ERROR; } - /* Skip everything up to (and then including) the function block */ - for (size_t i=0; i < function_block_idx; i++) - { + // Skip everything up to (and then including) the function block. + for (size_t i = 0; i < function_block_idx; i++) { block_t *b = parser.block_at_index(i); b->skip = true; } @@ -3780,11 +2801,8 @@ static int builtin_return(parser_t &parser, io_streams_t &streams, wchar_t **arg return status; } -/** - History of commands executed by user -*/ -static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **argv) -{ +// History of commands executed by user. +static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **argv) { int argc = builtin_count_args(argv); bool search_history = false; @@ -3794,18 +2812,15 @@ static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **ar bool clear_history = false; bool merge_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"merge", no_argument, 0, 'm' }, - { L"help", no_argument, 0, 'h' }, - { 0, 0, 0, 0 } - }; + 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"merge", no_argument, 0, 'm'}, + {L"help", no_argument, 0, 'h'}, + {0, 0, 0, 0}}; int opt = 0; int opt_index = 0; @@ -3813,53 +2828,60 @@ static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **ar wgetopter_t w; 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"); + // 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 = w.wgetopt_long_only(argc, argv, L"pdscvl", long_options, &opt_index)) != EOF) - { - switch (opt) - { - case 'p': + while ((opt = w.wgetopt_long_only(argc, argv, L"pdscvl", long_options, &opt_index)) != EOF) { + switch (opt) { + case 'p': { search_prefix = true; break; - case 'd': + } + case 'd': { delete_item = true; break; - case 's': + } + case 's': { search_history = true; break; - case 'c': + } + case 'c': { break; - case 'v': + } + case 'v': { save_history = true; break; - case 'l': + } + case 'l': { clear_history = true; break; - case 'm': + } + case 'm': { merge_history = true; break; - case 'h': + } + case 'h': { builtin_print_help(parser, streams, argv[0], streams.out); return STATUS_BUILTIN_OK; break; - case '?': - streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0], argv[w.woptind-1]); + } + case '?': { + streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0], argv[w.woptind - 1]); return STATUS_BUILTIN_ERROR; break; - default: - streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0], argv[w.woptind-1]); + } + default: { + streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0], argv[w.woptind - 1]); return STATUS_BUILTIN_ERROR; + } } } - /* Everything after is an argument */ + // Everything after is an argument. const wcstring_list_t args(argv + w.woptind, argv + argc); - if (argc == 1) - { + if (argc == 1) { wcstring full_history; history->get_string_representation(&full_history, wcstring(L"\n")); streams.out.append(full_history); @@ -3867,27 +2889,25 @@ static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **ar return STATUS_BUILTIN_OK; } - if (merge_history) - { + if (merge_history) { history->incorporate_external_changes(); return STATUS_BUILTIN_OK; } - if (search_history) - { + if (search_history) { int res = STATUS_BUILTIN_ERROR; - for (wcstring_list_t::const_iterator iter = args.begin(); iter != args.end(); ++iter) - { + for (wcstring_list_t::const_iterator iter = args.begin(); iter != args.end(); ++iter) { const wcstring &search_string = *iter; - if (search_string.empty()) - { - streams.err.append_format(BUILTIN_ERR_COMBO2, argv[0], L"Use --search with either --contains or --prefix"); + if (search_string.empty()) { + streams.err.append_format(BUILTIN_ERR_COMBO2, argv[0], + L"Use --search with either --contains or --prefix"); return res; } - history_search_t searcher = history_search_t(*history, search_string, search_prefix?HISTORY_SEARCH_TYPE_PREFIX:HISTORY_SEARCH_TYPE_CONTAINS); - while (searcher.go_backwards()) - { + history_search_t searcher = history_search_t( + *history, search_string, + search_prefix ? HISTORY_SEARCH_TYPE_PREFIX : HISTORY_SEARCH_TYPE_CONTAINS); + while (searcher.go_backwards()) { streams.out.append(searcher.current_string()); streams.out.append(L"\n"); res = STATUS_BUILTIN_OK; @@ -3896,10 +2916,8 @@ static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **ar return res; } - if (delete_item) - { - for (wcstring_list_t::const_iterator iter = args.begin(); iter != args.end(); ++iter) - { + if (delete_item) { + for (wcstring_list_t::const_iterator iter = args.begin(); iter != args.end(); ++iter) { wcstring delete_string = *iter; if (delete_string[0] == '"' && delete_string[delete_string.length() - 1] == '"') delete_string = delete_string.substr(1, delete_string.length() - 2); @@ -3909,14 +2927,12 @@ static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **ar return STATUS_BUILTIN_OK; } - if (save_history) - { + if (save_history) { history->save(); return STATUS_BUILTIN_OK; } - if (clear_history) - { + if (clear_history) { history->clear(); history->save(); return STATUS_BUILTIN_OK; @@ -3970,190 +2986,156 @@ int builtin_parse(parser_t &parser, io_streams_t &streams, wchar_t **argv) } #endif -int builtin_true(parser_t &parser, io_streams_t &streams, wchar_t **argv) -{ +int builtin_true(parser_t &parser, io_streams_t &streams, wchar_t **argv) { return STATUS_BUILTIN_OK; } -int builtin_false(parser_t &parser, io_streams_t &streams, wchar_t **argv) -{ +int builtin_false(parser_t &parser, io_streams_t &streams, wchar_t **argv) { return STATUS_BUILTIN_ERROR; } -/* - END OF BUILTIN COMMANDS - Below are functions for handling the builtin commands. - THESE MUST BE SORTED BY NAME! Completion lookup uses binary search. -*/ +// END OF BUILTIN COMMANDS +// Below are functions for handling the builtin commands. +// THESE MUST BE SORTED BY NAME! Completion lookup uses binary search. -/** - Data about all the builtin commands in fish. - Functions that are bound to builtin_generic are handled directly by the parser. - NOTE: These must be kept in sorted order! -*/ -static const builtin_data_t builtin_datas[]= -{ - { L"[", &builtin_test, N_(L"Test a condition") }, +// Data about all the builtin commands in fish. +// Functions that are bound to builtin_generic are handled directly by the parser. +// NOTE: These must be kept in sorted order! +static const builtin_data_t builtin_datas[] = { + {L"[", &builtin_test, N_(L"Test a condition")}, #if 0 // Disabled for the 2.2.0 release: https://github.com/fish-shell/fish-shell/issues/1809. { L"__fish_parse", &builtin_parse, N_(L"Try out the new parser") }, #endif - { L"and", &builtin_generic, N_(L"Execute command if previous command suceeded") }, - { L"begin", &builtin_generic, 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_generic, N_(L"Conditionally execute a block of commands") }, - { L"cd", &builtin_cd, N_(L"Change working directory") }, - { L"command", &builtin_command, 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_generic, N_(L"Evaluate block if condition is false") }, - { L"emit", &builtin_emit, N_(L"Emit an event") }, - { L"end", &builtin_generic, 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"false", &builtin_false, N_(L"Return an unsuccessful result") }, - { L"fg", &builtin_fg, N_(L"Send job to foreground") }, - { L"for", &builtin_generic, N_(L"Perform a set of commands multiple times") }, - { L"function", &builtin_generic, 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"printf", &builtin_printf, N_(L"Prints formatted text") }, - { 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"set_color", &builtin_set_color, N_(L"Set the terminal color") }, - { L"source", &builtin_source, N_(L"Evaluate contents of file") }, - { L"status", &builtin_status, N_(L"Return status information about fish") }, - { L"string", &builtin_string, N_(L"Manipulate strings") }, - { L"switch", &builtin_generic, N_(L"Conditionally execute a block of commands") }, - { L"test", &builtin_test, N_(L"Test a condition") }, - { L"true", &builtin_true, N_(L"Return a successful result") }, - { 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"and", &builtin_generic, N_(L"Execute command if previous command suceeded")}, + {L"begin", &builtin_generic, 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_generic, N_(L"Conditionally execute a block of commands")}, + {L"cd", &builtin_cd, N_(L"Change working directory")}, + {L"command", &builtin_command, 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_generic, N_(L"Evaluate block if condition is false")}, + {L"emit", &builtin_emit, N_(L"Emit an event")}, + {L"end", &builtin_generic, 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"false", &builtin_false, N_(L"Return an unsuccessful result")}, + {L"fg", &builtin_fg, N_(L"Send job to foreground")}, + {L"for", &builtin_generic, N_(L"Perform a set of commands multiple times")}, + {L"function", &builtin_generic, 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"printf", &builtin_printf, N_(L"Prints formatted text")}, + {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"set_color", &builtin_set_color, N_(L"Set the terminal color")}, + {L"source", &builtin_source, N_(L"Evaluate contents of file")}, + {L"status", &builtin_status, N_(L"Return status information about fish")}, + {L"string", &builtin_string, N_(L"Manipulate strings")}, + {L"switch", &builtin_generic, N_(L"Conditionally execute a block of commands")}, + {L"test", &builtin_test, N_(L"Test a condition")}, + {L"true", &builtin_true, N_(L"Return a successful result")}, + {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() -{ - for (size_t i=0; i < BUILTIN_COUNT; i++) - { +void builtin_init() { + for (size_t i = 0; i < BUILTIN_COUNT; i++) { intern_static(builtin_datas[i].name); } } -void builtin_destroy() -{ -} +void builtin_destroy() {} -int builtin_exists(const wcstring &cmd) -{ - return !!builtin_lookup(cmd); -} +int builtin_exists(const wcstring &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) -{ +// Return true if the specified builtin should handle it's own help, false otherwise. +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", L"printf"); + return contains(cmd, L"for", L"while", L"function", L"if", L"end", L"switch", L"case", L"count", + L"printf"); } +int builtin_run(parser_t &parser, const wchar_t *const *argv, io_streams_t &streams) { + int (*cmd)(parser_t & parser, io_streams_t & streams, const wchar_t *const *argv) = NULL; -int builtin_run(parser_t &parser, const wchar_t * const *argv, io_streams_t &streams) -{ - int (*cmd)(parser_t &parser, io_streams_t &streams, const wchar_t * const *argv)=NULL; - 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, io_streams_t &streams, const wchar_t * const*))(data ? data->func : NULL); + cmd = (int (*)(parser_t & parser, io_streams_t & streams, const wchar_t *const *))( + data ? data->func : NULL); - if (argv[1] != 0 && !internal_help(argv[0])) - { - if (argv[2] == 0 && (parse_util_argument_is_help(argv[1], 0))) - { + if (argv[1] != 0 && !internal_help(argv[0])) { + if (argv[2] == 0 && (parse_util_argument_is_help(argv[1], 0))) { builtin_print_help(parser, streams, argv[0], streams.out); return STATUS_BUILTIN_OK; } } - if (data != NULL) - { + if (data != NULL) { int status; status = cmd(parser, streams, argv); return status; - } - else - { + } else { debug(0, UNKNOWN_BUILTIN_ERR_MSG, argv[0]); } return STATUS_BUILTIN_ERROR; } - -wcstring_list_t builtin_get_names(void) -{ +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) -{ +void builtin_get_names(std::vector *list) { assert(list != NULL); list->reserve(list->size() + BUILTIN_COUNT); - for (size_t i=0; i < BUILTIN_COUNT; i++) - { + for (size_t i = 0; i < BUILTIN_COUNT; i++) { append_completion(list, 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) - { + if (builtin) { result = _(builtin->desc); } return result; diff --git a/src/builtin.h b/src/builtin.h index f747eb9b6..cea7318ad 100644 --- a/src/builtin.h +++ b/src/builtin.h @@ -1,144 +1,100 @@ -/** \file builtin.h - Prototypes for functions for executing builtin functions. -*/ - +// Prototypes for functions for executing builtin functions. #ifndef FISH_BUILTIN_H #define FISH_BUILTIN_H -#include // for size_t -#include // for vector +#include // for size_t +#include // for vector -#include "io.h" #include "common.h" +#include "io.h" class completion_t; class parser_t; -enum -{ - COMMAND_NOT_BUILTIN, - BUILTIN_REGULAR, - BUILTIN_FUNCTION -} -; +enum { COMMAND_NOT_BUILTIN, BUILTIN_REGULAR, BUILTIN_FUNCTION }; -/** - Error message on missing argument -*/ -#define BUILTIN_ERR_MISSING _( L"%ls: Expected argument for option %ls\n" ) +// Error message on missing argument. +#define BUILTIN_ERR_MISSING _(L"%ls: Expected argument for option %ls\n") -/** - Error message on invalid combination of options -*/ -#define BUILTIN_ERR_COMBO _( L"%ls: Invalid combination of options\n" ) +// Error message on invalid combination of options. +#define BUILTIN_ERR_COMBO _(L"%ls: Invalid combination of options\n") -/** - Error message on invalid combination of options -*/ -#define BUILTIN_ERR_COMBO2 _( L"%ls: Invalid combination of options,\n%ls\n" ) +// Error message on invalid combination of options. +#define BUILTIN_ERR_COMBO2 _(L"%ls: Invalid combination of options,\n%ls\n") -/** - Error message on multiple scope levels for variables -*/ -#define BUILTIN_ERR_GLOCAL _( L"%ls: Variable scope can only be one of universal, global and local\n" ) +// Error message on multiple scope levels for variables. +#define BUILTIN_ERR_GLOCAL \ + _(L"%ls: Variable scope can only be one of universal, global and local\n") -/** - Error message for specifying both export and unexport to set/read -*/ -#define BUILTIN_ERR_EXPUNEXP _( L"%ls: Variable can't be both exported and unexported\n" ) +// Error message for specifying both export and unexport to set/read. +#define BUILTIN_ERR_EXPUNEXP _(L"%ls: Variable can't be both exported and unexported\n") -/** - Error message for unknown switch -*/ -#define BUILTIN_ERR_UNKNOWN _( L"%ls: Unknown option '%ls'\n" ) +// Error message for unknown switch. +#define BUILTIN_ERR_UNKNOWN _(L"%ls: Unknown option '%ls'\n") -/** - Error message for invalid character in variable name -*/ -#define BUILTIN_ERR_VARCHAR _( L"%ls: Invalid character '%lc' in variable name. Only alphanumerical characters and underscores are valid in a variable name.\n" ) +// Error message for invalid character in variable name. +#define BUILTIN_ERR_VARCHAR \ + _(L"%ls: Invalid character '%lc' in variable name. Only alphanumerical characters and " \ + L"underscores are valid in a variable name.\n") -/** - Error message for invalid (empty) variable name -*/ -#define BUILTIN_ERR_VARNAME_ZERO _( L"%ls: Variable name can not be the empty string\n" ) +// Error message for invalid (empty) variable name. +#define BUILTIN_ERR_VARNAME_ZERO _(L"%ls: Variable name can not be the empty string\n") -/** - Error message when too many arguments are supplied to a builtin -*/ -#define BUILTIN_ERR_TOO_MANY_ARGUMENTS _( L"%ls: Too many arguments\n" ) +// Error message when too many arguments are supplied to a builtin. +#define BUILTIN_ERR_TOO_MANY_ARGUMENTS _(L"%ls: Too many arguments\n") -#define BUILTIN_ERR_NOT_NUMBER _( L"%ls: Argument '%ls' is not a number\n" ) +#define BUILTIN_ERR_NOT_NUMBER _(L"%ls: Argument '%ls' is not a number\n") -/** - Initialize builtin data. -*/ +// Initialize builtin data. void builtin_init(); -/** - Destroy builtin data. -*/ +// Destroy builtin data. void builtin_destroy(); -/** - Is there a builtin command with the given name? -*/ +// Is there a builtin command with the given name? int builtin_exists(const wcstring &cmd); -/** - Execute a builtin command +// Execute a builtin command +// +// \param parser The parser being used +// \param argv Array containing the command and parameters of the builtin. The list is terminated +// by a null pointer. This syntax resembles the syntax for exec. +// \param io the io redirections to perform on this builtin. +// +// \return the exit status of the builtin command +int builtin_run(parser_t &parser, const wchar_t *const *argv, io_streams_t &streams); - \param parser The parser being used - \param argv Array containing the command and parameters - of the builtin. The list is terminated by a - null pointer. This syntax resembles the syntax - for exec. - \param io the io redirections to perform on this builtin. - - \return the exit status of the builtin command -*/ -int builtin_run(parser_t &parser, const wchar_t * const *argv, io_streams_t &streams); - -/** Returns a list of all builtin names */ +// Returns a list of all builtin names. wcstring_list_t builtin_get_names(); -/** Insert all builtin names into list. */ +// Insert all builtin names into list. void builtin_get_names(std::vector *list); -/** - Return a one-line description of the specified builtin. -*/ +// Return a one-line description of the specified builtin. wcstring builtin_get_desc(const wcstring &b); - - -/** Support for setting and removing transient command lines. - This is used by 'complete -C' in order to make - the commandline builtin operate on the string to complete instead - of operating on whatever is to be completed. It's also used by - completion wrappers, to allow a command to appear as the command - being wrapped for the purposes of completion. - - Instantiating an instance of builtin_commandline_scoped_transient_t - pushes the command as the new transient commandline. The destructor removes it. - It will assert if construction/destruction does not happen in a stack-like (LIFO) order. -*/ -class builtin_commandline_scoped_transient_t -{ +// Support for setting and removing transient command lines. This is used by 'complete -C' in order +// to make the commandline builtin operate on the string to complete instead of operating on +// whatever is to be completed. It's also used by completion wrappers, to allow a command to appear +// as the command being wrapped for the purposes of completion. +// +// Instantiating an instance of builtin_commandline_scoped_transient_t pushes the command as the new +// transient commandline. The destructor removes it. It will assert if construction/destruction does +// not happen in a stack-like (LIFO) order. +class builtin_commandline_scoped_transient_t { size_t token; - public: + + public: explicit builtin_commandline_scoped_transient_t(const wcstring &cmd); ~builtin_commandline_scoped_transient_t(); }; - -/** - Run the __fish_print_help function to obtain the help information - for the specified command. -*/ +// Run the __fish_print_help function to obtain the help information for the specified command. wcstring builtin_help_get(parser_t &parser, const wchar_t *cmd); -/** Defines a function, like builtin_function. Returns 0 on success. args should NOT contain 'function' as the first argument. */ -int define_function(parser_t &parser, io_streams_t &streams, const wcstring_list_t &c_args, const wcstring &contents, int definition_line_offset, wcstring *out_err); - +// Defines a function, like builtin_function. Returns 0 on success. args should NOT contain +// 'function' as the first argument. +int define_function(parser_t &parser, io_streams_t &streams, const wcstring_list_t &c_args, + const wcstring &contents, int definition_line_offset, wcstring *out_err); #endif From c93e38380a1f1bed6c73846c4e3e6cf0b83b8471 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Mon, 18 Apr 2016 21:35:53 -0700 Subject: [PATCH 113/363] restyle autoload module to match project style Reduces lint errors from 38 to 19 (-50%). Line count from 506 to 426 (-16%). Another step in resolving issue #2902. --- src/autoload.cpp | 340 +++++++++++++++++++---------------------------- src/autoload.h | 146 +++++++++----------- 2 files changed, 203 insertions(+), 283 deletions(-) diff --git a/src/autoload.cpp b/src/autoload.cpp index c50293624..1c0817349 100644 --- a/src/autoload.cpp +++ b/src/autoload.cpp @@ -1,103 +1,81 @@ -/** \file autoload.cpp - -The classes responsible for autoloading functions and completions. -*/ - -#include "config.h" // IWYU pragma: keep +// The classes responsible for autoloading functions and completions. #include "autoload.h" -#include "wutil.h" -#include "common.h" -#include "signal.h" // IWYU pragma: keep - needed for CHECK_BLOCK -#include "env.h" -#include "exec.h" #include #include #include #include #include +#include #include #include #include -#include +#include "common.h" +#include "config.h" // IWYU pragma: keep +#include "env.h" +#include "exec.h" +#include "signal.h" // IWYU pragma: keep - needed for CHECK_BLOCK +#include "wutil.h" -/* The time before we'll recheck an autoloaded file */ +// 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) -{ - //printf("Touch %ls\n", path.c_str()); +file_access_attempt_t access_file(const wcstring &path, int mode) { + // printf("Touch %ls\n", path.c_str()); file_access_attempt_t result = {}; 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; } } - // Note that we record the last checked time after the call, on the assumption that in a slow filesystem, the lag comes before the kernel check, not after. + // Note that we record the last checked time after the call, on the assumption that in a slow + // filesystem, the lag comes before the kernel check, not after. result.stale = false; result.last_checked = time(NULL); return result; } -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) -{ +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) { pthread_mutex_init(&lock, NULL); } -autoload_t::~autoload_t() -{ - pthread_mutex_destroy(&lock); -} +autoload_t::~autoload_t() { pthread_mutex_destroy(&lock); } -void autoload_t::node_was_evicted(autoload_function_t *node) -{ - // This should only ever happen on the main thread +void autoload_t::node_was_evicted(autoload_function_t *node) { + // This should only ever happen on the main thread. ASSERT_IS_MAIN_THREAD(); - // Tell ourselves that the command was removed if it was loaded - if (node->is_loaded) - this->command_removed(node->key); + // Tell ourselves that the command was removed if it was loaded. + if (node->is_loaded) this->command_removed(node->key); delete node; } -int autoload_t::unload(const wcstring &cmd) -{ - return this->evict_node(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); ASSERT_IS_MAIN_THREAD(); env_var_t path_var = env_get_string(env_var_name); - /* - Do we know where to look? - */ - if (path_var.empty()) - return 0; + // Do we know where to look? + if (path_var.empty()) return 0; - /* Check if the lookup path has changed. If so, drop all loaded files. path_var may only be inspected on the main thread. */ - if (path_var != this->last_path) - { + // 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) { this->last_path = path_var; this->last_path_tokenized.clear(); tokenize_variable_array(this->last_path, this->last_path_tokenized); @@ -106,248 +84,215 @@ int autoload_t::load(const wcstring &cmd, bool reload) this->evict_all_nodes(); } - /* Mark that we're loading this. Hang onto the iterator for fast erasing later. Note that std::set has guarantees about not invalidating iterators, so this is safe to do across the callouts below. */ + // Mark that we're loading this. Hang onto the iterator for fast erasing later. Note that + // std::set has guarantees about not invalidating iterators, so this is safe to do across the + // callouts below. typedef std::set::iterator set_iterator_t; std::pair insert_result = is_loading_set.insert(cmd); set_iterator_t where = insert_result.first; bool inserted = insert_result.second; - /** Warn and fail on infinite recursion. It's OK to do this because this function is only called on the main thread. */ - if (! inserted) - { - /* We failed to insert */ - 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."), + // Warn and fail on infinite recursion. It's OK to do this because this function is only called + // on the main thread. + if (!inserted) { + // We failed to insert. + 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; } - /* Try loading it */ + // Try loading it. res = this->locate_file_and_maybe_load_it(cmd, true, reload, this->last_path_tokenized); - - /* Clean up */ + // Clean up. is_loading_set.erase(where); - 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; + 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); } -static bool script_name_precedes_script_name(const builtin_script_t &script1, const builtin_script_t &script2) -{ +static bool script_name_precedes_script_name(const builtin_script_t &script1, + const builtin_script_t &script2) { return wcscmp(script1.name, script2.name) < 0; } -/** Check whether the given command is loaded. */ -bool autoload_t::has_tried_loading(const wcstring &cmd) -{ +// Check whether the given command is loaded. +bool autoload_t::has_tried_loading(const wcstring &cmd) { scoped_lock locker(lock); - autoload_function_t * func = this->get_node(cmd); + autoload_function_t *func = this->get_node(cmd); return func != NULL; } -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; +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; } -autoload_function_t *autoload_t::get_autoloaded_function_with_creation(const wcstring &cmd, bool allow_eviction) -{ +autoload_function_t *autoload_t::get_autoloaded_function_with_creation(const wcstring &cmd, + bool allow_eviction) { 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); } } return func; } -/** - This internal helper function does all the real work. By using two - functions, the internal function can return on various places in - the code, and the caller can take care of various cleanup work. - - cmd: the command name ('grep') - really_load: whether to actually parse it as a function, or just check it it exists - reload: whether to reload it if it's already loaded - path_list: the set of paths to check - - Result: if really_load is true, returns whether the function was loaded. Otherwise returns whether the function existed. -*/ -bool autoload_t::locate_file_and_maybe_load_it(const wcstring &cmd, bool really_load, bool reload, const wcstring_list_t &path_list) -{ - /* Note that we are NOT locked in this function! */ +// This internal helper function does all the real work. By using two functions, the internal +// function can return on various places in the code, and the caller can take care of various +// cleanup work. +// +// cmd: the command name ('grep') +// really_load: whether to actually parse it as a function, or just check it it exists +// reload: whether to reload it if it's already loaded +// path_list: the set of paths to check +// +// Result: if really_load is true, returns whether the function was loaded. Otherwise returns +// whether the function existed. +bool autoload_t::locate_file_and_maybe_load_it(const wcstring &cmd, bool really_load, bool reload, + const wcstring_list_t &path_list) { + // Note that we are NOT locked in this function! 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. */ + // Try using a cached function. If we really want the function to be loaded, require that it be + // really loaded. If we're not reloading, allow stale functions. { - bool allow_stale_functions = ! reload; + bool allow_stale_functions = !reload; - /* Take a lock */ scoped_lock locker(lock); + autoload_function_t *func = this->get_node(cmd); // get the function - /* Get the function */ - autoload_function_t * func = this->get_node(cmd); - - /* Determine if we can use this cached function */ + // Determine if we can use this cached function. bool use_cached; - if (! func) - { - /* Can't use a function that doesn't exist */ + if (!func) { + // Can't use a function that doesn't exist. use_cached = false; - } - 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)) - { - /* Can't use a stale function */ - use_cached = false; - } - else - { - /* I guess we can use it */ - use_cached = true; + } else if (really_load && !func->is_placeholder && !func->is_loaded) { + use_cached = false; // can't use an unloaded function + } else if (!allow_stale_functions && is_stale(func)) { + use_cached = false; // can't use a stale function + } else { + use_cached = true; // I guess we can use it } - /* If we can use this function, return whether we were able to access it */ - if (use_cached) - { + // If we can use this function, return whether we were able to access it. + if (use_cached) { assert(func != NULL); return func->is_internalized || func->access.accessible; } } - /* The source of the script will end up here */ + // The source of the script will end up here. wcstring script_source; bool has_script_source = false; - /* Whether we found an accessible file */ + // Whether we found an accessible file. bool found_file = false; - /* Look for built-in scripts via a binary search */ + // Look for built-in scripts via a binary search. const builtin_script_t *matching_builtin_script = NULL; - if (builtin_script_count > 0) - { + if (builtin_script_count > 0) { const builtin_script_t test_script = {cmd.c_str(), NULL}; const builtin_script_t *array_end = builtin_scripts + builtin_script_count; - const builtin_script_t *found = std::lower_bound(builtin_scripts, array_end, test_script, script_name_precedes_script_name); - if (found != array_end && ! wcscmp(found->name, test_script.name)) - { - /* We found it */ + const builtin_script_t *found = std::lower_bound(builtin_scripts, array_end, test_script, + script_name_precedes_script_name); + if (found != array_end && !wcscmp(found->name, test_script.name)) { matching_builtin_script = found; } } - if (matching_builtin_script) - { + if (matching_builtin_script) { has_script_source = true; script_source = str2wcstring(matching_builtin_script->def); - /* Make a node representing this function */ + // Make a node representing this function. scoped_lock locker(lock); autoload_function_t *func = this->get_autoloaded_function_with_creation(cmd, really_load); - /* This function is internalized */ + // This function is internalized. func->is_internalized = true; - /* It's a fiction to say the script is loaded at this point, but we're definitely going to load it down below. */ + // It's a fiction to say the script is loaded at this point, but we're definitely going to + // load it down below. if (really_load) func->is_loaded = true; } - if (! has_script_source) - { - /* Iterate over path searching for suitable completion files */ - for (size_t i=0; iget_node(cmd); - /* Generate the source if we need to load it */ - bool need_to_load_function = really_load && (func == NULL || func->access.mod_time != access.mod_time || ! func->is_loaded); - if (need_to_load_function) - { - - /* Generate the script source */ + // Generate the source if we need to load it. + bool need_to_load_function = + really_load && + (func == NULL || func->access.mod_time != access.mod_time || !func->is_loaded); + if (need_to_load_function) { + // Generate the script source. wcstring esc = escape_string(path, 1); script_source = L"source " + esc; has_script_source = true; - /* Remove any loaded command because we are going to reload it. Note that this will deadlock if command_removed calls back into us. */ - if (func && func->is_loaded) - { + // Remove any loaded command because we are going to reload it. Note that this + // will deadlock if command_removed calls back into us. + if (func && func->is_loaded) { command_removed(cmd); func->is_placeholder = false; } - /* Mark that we're reloading it */ + // Mark that we're reloading it. reloaded = true; } - /* Create the function if we haven't yet. This does not load it. Do not trigger eviction unless we are actually loading, because we don't want to evict off of the main thread. */ - if (! func) - { + // Create the function if we haven't yet. This does not load it. Do not trigger + // eviction unless we are actually loading, because we don't want to evict off of + // the main thread. + if (!func) { func = get_autoloaded_function_with_creation(cmd, really_load); } - /* It's a fiction to say the script is loaded at this point, but we're definitely going to load it down below. */ + // It's a fiction to say the script is loaded at this point, but we're definitely + // going to load it down below. if (need_to_load_function) func->is_loaded = true; - /* Unconditionally record our access time */ + // Unconditionally record our access time. func->access = access; break; } } - /* - If no file or builtin script was found we insert a placeholder function. - 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 no file or builtin script was found we insert a placeholder function. 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) { scoped_lock locker(lock); - /* Generate a placeholder */ + // 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); } } @@ -355,22 +300,15 @@ 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, false /* do not apply exit status */) == -1) - { - /* Do nothing on failure */ - } - + // If we have a script, either built-in or a file source, then run it. + if (really_load && has_script_source) { + // Do nothing on failure. + exec_subshell(script_source, false /* do not apply exit status */); } - if (really_load) - { + if (really_load) { return reloaded; - } - else - { + } else { return found_file || has_script_source; } } diff --git a/src/autoload.h b/src/autoload.h index f0ad9d030..5f8b4d310 100644 --- a/src/autoload.h +++ b/src/autoload.h @@ -1,8 +1,4 @@ -/** \file autoload.h - - The classes responsible for autoloading functions and completions. -*/ - +// The classes responsible for autoloading functions and completions. #ifndef FISH_AUTOLOAD_H #define FISH_AUTOLOAD_H @@ -13,118 +9,104 @@ #include "common.h" #include "lru.h" -/** A struct responsible for recording an attempt to access a file. */ -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 */ - bool stale; /** Whether the access attempt is stale */ - int error; /** If we could not access the file, the error code */ +// A struct responsible for recording an attempt to access a file. +struct file_access_attempt_t { + time_t mod_time; // 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 + bool stale; // whether the access attempt is stale + int error; // if we could not access the file, the error code }; file_access_attempt_t access_file(const wcstring &path, int mode); -struct autoload_function_t : public lru_node_t -{ - explicit autoload_function_t(const wcstring &key) : lru_node_t(key), access(), is_loaded(false), is_placeholder(false), is_internalized(false) { } - file_access_attempt_t access; /** The last access attempt */ - bool is_loaded; /** Whether we have actually loaded this function */ - bool is_placeholder; /** Whether we are a placeholder that stands in for "no such function". If this is true, then is_loaded must be false. */ - bool is_internalized; /** Whether this function came from a builtin "internalized" script */ +struct autoload_function_t : public lru_node_t { + explicit autoload_function_t(const wcstring &key) + : lru_node_t(key), + access(), + is_loaded(false), + is_placeholder(false), + is_internalized(false) {} + file_access_attempt_t access; // the last access attempt + bool is_loaded; // whether we have actually loaded this function + // Whether we are a placeholder that stands in for "no such function". If this is true, then + // is_loaded must be false. + bool is_placeholder; + // Whether this function came from a builtin "internalized" script. + bool is_internalized; }; -struct builtin_script_t -{ +struct builtin_script_t { const wchar_t *name; const char *def; }; 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 -{ -private: - - /** Lock for thread safety */ +// A class that represents a path from which we can autoload, and the autoloaded contents. +class autoload_t : private lru_cache_t { + private: + // Lock for thread safety. pthread_mutex_t lock; - - /** The environment variable name */ + // The environment variable name. const wcstring env_var_name; - - /** Builtin script array */ + // Builtin script array. const struct builtin_script_t *const builtin_scripts; - - /** Builtin script count */ + // Builtin script count. const size_t builtin_script_count; - - /** The path from which we most recently autoloaded */ + // The path from which we most recently autoloaded. wcstring last_path; - - /** That path, tokenized (split on separators) */ + // That path, tokenized (split on separators). wcstring_list_t last_path_tokenized; - /** - 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; - void remove_all_functions(void) - { - this->evict_all_nodes(); - } + 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); + autoload_function_t *get_autoloaded_function_with_creation(const wcstring &cmd, + bool allow_eviction); -protected: - /** Overridable callback for when a command is removed */ - virtual void command_removed(const wcstring &cmd) { } + 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); - /** 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); + virtual ~autoload_t(); // destructor - /** Destructor */ - virtual ~autoload_t(); - - /** - Autoload the specified file, if it exists in the specified path. Do - not load it multiple times unless its timestamp changes or - parse_util_unload is called. - - Autoloading one file may unload another. - - \param cmd the filename to search for. The suffix '.fish' is always added to this name - \param on_unload a callback function to run if a suitable file is found, which has not already been run. unload will also be called for old files which are unloaded. - \param reload wheter to recheck file timestamps on already loaded files - */ + // Autoload the specified file, if it exists in the specified path. Do not load it multiple + // times unless its timestamp changes or parse_util_unload is called. + // + // Autoloading one file may unload another. + // + // \param cmd the filename to search for. The suffix '.fish' is always added to this name + // \param on_unload a callback function to run if a suitable file is found, which has not + // already been run. unload will also be called for old files which are unloaded. + // \param reload wheter to recheck file timestamps on already loaded files int load(const wcstring &cmd, bool reload); - /** Check whether we have tried loading the given command. Does not do any I/O. */ + // Check whether we have tried loading the given command. Does not do any I/O. bool has_tried_loading(const wcstring &cmd); - /** - Tell the autoloader that the specified file, in the specified path, - is no longer loaded. - - \param cmd the filename to search for. The suffix '.fish' is always added to this name - \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 - */ + // Tell the autoloader that the specified file, in the specified path, is no longer loaded. + // + // \param cmd the filename to search for. The suffix '.fish' is always added to this name + // \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); - /** Check whether the given command could be loaded, but do not load it. */ + // 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); - }; - #endif From aefcf544ca27746ce1d69ecd6bda688b93827840 Mon Sep 17 00:00:00 2001 From: David Adam Date: Tue, 19 Apr 2016 15:12:59 +0800 Subject: [PATCH 114/363] Update Xcode build for new pcre2 version --- fish.xcodeproj/project.pbxproj | 53 +++++++++++++++++++--------------- osx/shared_headers/pcre2.h | 30 +++++++++++++++---- 2 files changed, 54 insertions(+), 29 deletions(-) diff --git a/fish.xcodeproj/project.pbxproj b/fish.xcodeproj/project.pbxproj index 2d85b93a8..b106ec40c 100644 --- a/fish.xcodeproj/project.pbxproj +++ b/fish.xcodeproj/project.pbxproj @@ -66,6 +66,7 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 63A2C0E91CC60F3B00973404 /* pcre2_find_bracket.c in Sources */ = {isa = PBXBuildFile; fileRef = 63A2C0E81CC5F9FB00973404 /* pcre2_find_bracket.c */; }; D00769121990137800CA4627 /* autoload.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0C6FCC914CFA4B0004CE8AD /* autoload.cpp */; }; D00769131990137800CA4627 /* builtin_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0F3373A1506DE3C00ECEFC0 /* builtin_test.cpp */; }; D00769141990137800CA4627 /* color.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0B6B0FE14E88BA400AD6C10 /* color.cpp */; }; @@ -448,6 +449,7 @@ /* Begin PBXFileReference section */ 4E142D731B56B5D7008783C8 /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = config.h; path = ../osx/config.h; sourceTree = ""; }; + 63A2C0E81CC5F9FB00973404 /* pcre2_find_bracket.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_find_bracket.c; sourceTree = ""; }; D00769421990137800CA4627 /* fish_tests */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = fish_tests; sourceTree = BUILT_PRODUCTS_DIR; }; D00F63F019137E9D00FCCDEC /* fish_version.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fish_version.cpp; sourceTree = ""; }; D01A2D23169B730A00767098 /* man1 */ = {isa = PBXFileReference; lastKnownFileType = text; name = man1; path = pages_for_manpath/man1; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -459,30 +461,30 @@ D032388A1849D1980032CF2C /* pager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pager.h; sourceTree = ""; }; D03EE83814DF88B200FC7150 /* lru.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = lru.h; sourceTree = ""; }; D04F7F7B1BA4BF4000B0F227 /* builtin_string.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_string.cpp; sourceTree = ""; }; - D04F7F8D1BA4DCD900B0F227 /* pcre2_compile.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pcre2_compile.c; path = "pcre2-10.20/src/pcre2_compile.c"; sourceTree = SOURCE_ROOT; }; - D04F7F901BA4DCE900B0F227 /* pcre2_config.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pcre2_config.c; path = "pcre2-10.20/src/pcre2_config.c"; sourceTree = SOURCE_ROOT; }; - D04F7F931BA4DCFA00B0F227 /* pcre2_context.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pcre2_context.c; path = "pcre2-10.20/src/pcre2_context.c"; sourceTree = SOURCE_ROOT; }; - D04F7F961BA4DD1100B0F227 /* pcre2_dfa_match.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pcre2_dfa_match.c; path = "pcre2-10.20/src/pcre2_dfa_match.c"; sourceTree = SOURCE_ROOT; }; - D04F7F991BA4DD2000B0F227 /* pcre2_error.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pcre2_error.c; path = "pcre2-10.20/src/pcre2_error.c"; sourceTree = SOURCE_ROOT; }; - D04F7F9C1BA4DD4A00B0F227 /* pcre2_maketables.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pcre2_maketables.c; path = "pcre2-10.20/src/pcre2_maketables.c"; sourceTree = SOURCE_ROOT; }; - D04F7F9F1BA4DD5900B0F227 /* pcre2_match.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pcre2_match.c; path = "pcre2-10.20/src/pcre2_match.c"; sourceTree = SOURCE_ROOT; }; - D04F7FA21BA4DD6900B0F227 /* pcre2_match_data.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pcre2_match_data.c; path = "pcre2-10.20/src/pcre2_match_data.c"; sourceTree = SOURCE_ROOT; }; - D04F7FA51BA4DD7300B0F227 /* pcre2_newline.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pcre2_newline.c; path = "pcre2-10.20/src/pcre2_newline.c"; sourceTree = SOURCE_ROOT; }; - D04F7FA81BA4DD8400B0F227 /* pcre2_ord2utf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pcre2_ord2utf.c; path = "pcre2-10.20/src/pcre2_ord2utf.c"; sourceTree = SOURCE_ROOT; }; - D04F7FAB1BA4DDA500B0F227 /* pcre2_pattern_info.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pcre2_pattern_info.c; path = "pcre2-10.20/src/pcre2_pattern_info.c"; sourceTree = SOURCE_ROOT; }; - D04F7FAE1BA4DDB500B0F227 /* pcre2_serialize.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pcre2_serialize.c; path = "pcre2-10.20/src/pcre2_serialize.c"; sourceTree = SOURCE_ROOT; }; - D04F7FB11BA4DDBF00B0F227 /* pcre2_string_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pcre2_string_utils.c; path = "pcre2-10.20/src/pcre2_string_utils.c"; sourceTree = SOURCE_ROOT; }; - D04F7FB41BA4DDC900B0F227 /* pcre2_study.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pcre2_study.c; path = "pcre2-10.20/src/pcre2_study.c"; sourceTree = SOURCE_ROOT; }; - D04F7FB71BA4DDEB00B0F227 /* pcre2_substitute.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pcre2_substitute.c; path = "pcre2-10.20/src/pcre2_substitute.c"; sourceTree = SOURCE_ROOT; }; - D04F7FB81BA4DDEB00B0F227 /* pcre2_substring.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pcre2_substring.c; path = "pcre2-10.20/src/pcre2_substring.c"; sourceTree = SOURCE_ROOT; }; - D04F7FB91BA4DDEB00B0F227 /* pcre2_tables.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pcre2_tables.c; path = "pcre2-10.20/src/pcre2_tables.c"; sourceTree = SOURCE_ROOT; }; - D04F7FBA1BA4DDEB00B0F227 /* pcre2_ucd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pcre2_ucd.c; path = "pcre2-10.20/src/pcre2_ucd.c"; sourceTree = SOURCE_ROOT; }; - D04F7FBB1BA4DDEB00B0F227 /* pcre2_valid_utf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pcre2_valid_utf.c; path = "pcre2-10.20/src/pcre2_valid_utf.c"; sourceTree = SOURCE_ROOT; }; - D04F7FBC1BA4DDEB00B0F227 /* pcre2_xclass.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pcre2_xclass.c; path = "pcre2-10.20/src/pcre2_xclass.c"; sourceTree = SOURCE_ROOT; }; - D04F7FC91BA4DE3500B0F227 /* pcre2_jit_compile.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pcre2_jit_compile.c; path = "pcre2-10.20/src/pcre2_jit_compile.c"; sourceTree = SOURCE_ROOT; }; + D04F7F8D1BA4DCD900B0F227 /* pcre2_compile.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_compile.c; sourceTree = ""; }; + D04F7F901BA4DCE900B0F227 /* pcre2_config.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_config.c; sourceTree = ""; }; + D04F7F931BA4DCFA00B0F227 /* pcre2_context.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_context.c; sourceTree = ""; }; + D04F7F961BA4DD1100B0F227 /* pcre2_dfa_match.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_dfa_match.c; sourceTree = ""; }; + D04F7F991BA4DD2000B0F227 /* pcre2_error.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_error.c; sourceTree = ""; }; + D04F7F9C1BA4DD4A00B0F227 /* pcre2_maketables.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_maketables.c; sourceTree = ""; }; + D04F7F9F1BA4DD5900B0F227 /* pcre2_match.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_match.c; sourceTree = ""; }; + D04F7FA21BA4DD6900B0F227 /* pcre2_match_data.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_match_data.c; sourceTree = ""; }; + D04F7FA51BA4DD7300B0F227 /* pcre2_newline.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_newline.c; sourceTree = ""; }; + D04F7FA81BA4DD8400B0F227 /* pcre2_ord2utf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_ord2utf.c; sourceTree = ""; }; + D04F7FAB1BA4DDA500B0F227 /* pcre2_pattern_info.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_pattern_info.c; sourceTree = ""; }; + D04F7FAE1BA4DDB500B0F227 /* pcre2_serialize.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_serialize.c; sourceTree = ""; }; + D04F7FB11BA4DDBF00B0F227 /* pcre2_string_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_string_utils.c; sourceTree = ""; }; + D04F7FB41BA4DDC900B0F227 /* pcre2_study.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_study.c; sourceTree = ""; }; + D04F7FB71BA4DDEB00B0F227 /* pcre2_substitute.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_substitute.c; sourceTree = ""; }; + D04F7FB81BA4DDEB00B0F227 /* pcre2_substring.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_substring.c; sourceTree = ""; }; + D04F7FB91BA4DDEB00B0F227 /* pcre2_tables.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_tables.c; sourceTree = ""; }; + D04F7FBA1BA4DDEB00B0F227 /* pcre2_ucd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_ucd.c; sourceTree = ""; }; + D04F7FBB1BA4DDEB00B0F227 /* pcre2_valid_utf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_valid_utf.c; sourceTree = ""; }; + D04F7FBC1BA4DDEB00B0F227 /* pcre2_xclass.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_xclass.c; sourceTree = ""; }; + D04F7FC91BA4DE3500B0F227 /* pcre2_jit_compile.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_jit_compile.c; sourceTree = ""; }; D04F7FD01BA4E29300B0F227 /* libpcre2.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libpcre2.a; sourceTree = BUILT_PRODUCTS_DIR; }; - D04F7FF31BA4E6F300B0F227 /* pcre2_auto_possess.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pcre2_auto_possess.c; path = "pcre2-10.20/src/pcre2_auto_possess.c"; sourceTree = SOURCE_ROOT; }; - D04F7FF71BA4E82C00B0F227 /* pcre2_chartables.c.dist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = pcre2_chartables.c.dist; path = "pcre2-10.20/src/pcre2_chartables.c.dist"; sourceTree = SOURCE_ROOT; }; + D04F7FF31BA4E6F300B0F227 /* pcre2_auto_possess.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_auto_possess.c; sourceTree = ""; }; + D04F7FF71BA4E82C00B0F227 /* pcre2_chartables.c.dist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = pcre2_chartables.c.dist; sourceTree = ""; }; D052D8091868F7FC003ABCBD /* parse_execution.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = parse_execution.cpp; sourceTree = ""; }; D052D80A1868F7FC003ABCBD /* parse_execution.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = parse_execution.h; sourceTree = ""; }; D07B247215BCC15700D4ADB4 /* add-shell */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = "add-shell"; path = "build_tools/osx_package_scripts/add-shell"; sourceTree = ""; }; @@ -663,6 +665,7 @@ D04F7F8B1BA4DC7600B0F227 /* pcre */ = { isa = PBXGroup; children = ( + 63A2C0E81CC5F9FB00973404 /* pcre2_find_bracket.c */, D04F7FF31BA4E6F300B0F227 /* pcre2_auto_possess.c */, D04F7F8D1BA4DCD900B0F227 /* pcre2_compile.c */, D04F7F901BA4DCE900B0F227 /* pcre2_config.c */, @@ -688,7 +691,8 @@ D04F7FF71BA4E82C00B0F227 /* pcre2_chartables.c.dist */, ); name = pcre; - sourceTree = ""; + path = "pcre2-10.21/src"; + sourceTree = SOURCE_ROOT; }; D08A328E17B4455100F3A533 /* fish_tests */ = { isa = PBXGroup; @@ -1273,6 +1277,7 @@ D04F7FDE1BA4E3AC00B0F227 /* pcre2_newline.c in Sources */, D04F7FDF1BA4E3AC00B0F227 /* pcre2_ord2utf.c in Sources */, D04F7FE01BA4E3AC00B0F227 /* pcre2_pattern_info.c in Sources */, + 63A2C0E91CC60F3B00973404 /* pcre2_find_bracket.c in Sources */, D04F7FF41BA4E6F300B0F227 /* pcre2_auto_possess.c in Sources */, D04F7FE11BA4E3AC00B0F227 /* pcre2_serialize.c in Sources */, D04F7FE21BA4E3AC00B0F227 /* pcre2_string_utils.c in Sources */, diff --git a/osx/shared_headers/pcre2.h b/osx/shared_headers/pcre2.h index 3e97fb8bf..7f9ba4f19 100644 --- a/osx/shared_headers/pcre2.h +++ b/osx/shared_headers/pcre2.h @@ -5,7 +5,7 @@ /* This is the public header file for the PCRE library, second API, to be #included by applications that call PCRE2 functions. - Copyright (c) 2015 University of Cambridge + Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -42,9 +42,9 @@ POSSIBILITY OF SUCH DAMAGE. /* The current PCRE version information. */ #define PCRE2_MAJOR 10 -#define PCRE2_MINOR 20 +#define PCRE2_MINOR 21 #define PCRE2_PRERELEASE -#define PCRE2_DATE 2015-06-30 +#define PCRE2_DATE 2016-01-12 /* When an application links to a PCRE DLL in Windows, the symbols that are imported have to be identified as such. When building PCRE2, the appropriate @@ -120,6 +120,8 @@ D is inspected during pcre2_dfa_match() execution #define PCRE2_UTF 0x00080000u /* C J M D */ #define PCRE2_NEVER_BACKSLASH_C 0x00100000u /* C */ #define PCRE2_ALT_CIRCUMFLEX 0x00200000u /* J M D */ +#define PCRE2_ALT_VERBNAMES 0x00400000u /* C */ +#define PCRE2_USE_OFFSET_LIMIT 0x00800000u /* J M D */ /* These are for pcre2_jit_compile(). */ @@ -144,9 +146,13 @@ sanity checks). */ #define PCRE2_DFA_RESTART 0x00000040u #define PCRE2_DFA_SHORTEST 0x00000080u -/* This is an additional option for pcre2_substitute(). */ +/* These are additional options for pcre2_substitute(). */ -#define PCRE2_SUBSTITUTE_GLOBAL 0x00000100u +#define PCRE2_SUBSTITUTE_GLOBAL 0x00000100u +#define PCRE2_SUBSTITUTE_EXTENDED 0x00000200u +#define PCRE2_SUBSTITUTE_UNSET_EMPTY 0x00000400u +#define PCRE2_SUBSTITUTE_UNKNOWN_UNSET 0x00000800u +#define PCRE2_SUBSTITUTE_OVERFLOW_LENGTH 0x00001000u /* Newline and \R settings, for use in compile contexts. The newline values must be kept in step with values set in config.h and both sets must all be @@ -233,6 +239,12 @@ numbers must not be changed. */ #define PCRE2_ERROR_RECURSIONLIMIT (-53) #define PCRE2_ERROR_UNAVAILABLE (-54) #define PCRE2_ERROR_UNSET (-55) +#define PCRE2_ERROR_BADOFFSETLIMIT (-56) +#define PCRE2_ERROR_BADREPESCAPE (-57) +#define PCRE2_ERROR_REPMISSINGBRACE (-58) +#define PCRE2_ERROR_BADSUBSTITUTION (-59) +#define PCRE2_ERROR_BADSUBSPATTERN (-60) +#define PCRE2_ERROR_TOOMANYREPLACE (-61) /* Request types for pcre2_pattern_info() */ @@ -259,6 +271,7 @@ numbers must not be changed. */ #define PCRE2_INFO_NEWLINE 20 #define PCRE2_INFO_RECURSIONLIMIT 21 #define PCRE2_INFO_SIZE 22 +#define PCRE2_INFO_HASBACKSLASHC 23 /* Request types for pcre2_config(). */ @@ -291,6 +304,7 @@ define special values to indicate zero-terminated strings and unset offsets in the offset vector (ovector). */ #define PCRE2_SIZE size_t +#define PCRE2_SIZE_MAX SIZE_MAX #define PCRE2_ZERO_TERMINATED (~(PCRE2_SIZE)0) #define PCRE2_UNSET (~(PCRE2_SIZE)0) @@ -388,6 +402,8 @@ PCRE2_EXP_DECL void pcre2_compile_context_free(pcre2_compile_context *); \ PCRE2_EXP_DECL int pcre2_set_bsr(pcre2_compile_context *, uint32_t); \ PCRE2_EXP_DECL int pcre2_set_character_tables(pcre2_compile_context *, \ const unsigned char *); \ +PCRE2_EXP_DECL int pcre2_set_max_pattern_length(pcre2_compile_context *, \ + PCRE2_SIZE); \ PCRE2_EXP_DECL int pcre2_set_newline(pcre2_compile_context *, uint32_t); \ PCRE2_EXP_DECL int pcre2_set_parens_nest_limit(pcre2_compile_context *, \ uint32_t); \ @@ -405,6 +421,8 @@ PCRE2_EXP_DECL int pcre2_set_callout(pcre2_match_context *, \ int (*)(pcre2_callout_block *, void *), void *); \ PCRE2_EXP_DECL int pcre2_set_match_limit(pcre2_match_context *, \ uint32_t); \ +PCRE2_EXP_DECL int pcre2_set_offset_limit(pcre2_match_context *, \ + PCRE2_SIZE); \ PCRE2_EXP_DECL int pcre2_set_recursion_limit(pcre2_match_context *, \ uint32_t); \ PCRE2_EXP_DECL int pcre2_set_recursion_memory_management( \ @@ -606,8 +624,10 @@ pcre2_compile are called by application code. */ #define pcre2_set_character_tables PCRE2_SUFFIX(pcre2_set_character_tables_) #define pcre2_set_compile_recursion_guard PCRE2_SUFFIX(pcre2_set_compile_recursion_guard_) #define pcre2_set_match_limit PCRE2_SUFFIX(pcre2_set_match_limit_) +#define pcre2_set_max_pattern_length PCRE2_SUFFIX(pcre2_set_max_pattern_length_) #define pcre2_set_newline PCRE2_SUFFIX(pcre2_set_newline_) #define pcre2_set_parens_nest_limit PCRE2_SUFFIX(pcre2_set_parens_nest_limit_) +#define pcre2_set_offset_limit PCRE2_SUFFIX(pcre2_set_offset_limit_) #define pcre2_set_recursion_limit PCRE2_SUFFIX(pcre2_set_recursion_limit_) #define pcre2_set_recursion_memory_management PCRE2_SUFFIX(pcre2_set_recursion_memory_management_) #define pcre2_substitute PCRE2_SUFFIX(pcre2_substitute_) From fc78e70d082741e6c7c71c537668fd9169e58610 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Sat, 16 Apr 2016 01:02:58 -0700 Subject: [PATCH 115/363] Apply pcre2 svn rev 489 patch to fix CVE-2016-3191 --- pcre2-10.21/src/pcre2_compile.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/pcre2-10.21/src/pcre2_compile.c b/pcre2-10.21/src/pcre2_compile.c index d8528378e..876109036 100644 --- a/pcre2-10.21/src/pcre2_compile.c +++ b/pcre2-10.21/src/pcre2_compile.c @@ -4866,7 +4866,6 @@ for (;; ptr++) /* For a single, positive character, get the value into mcbuffer, and then we can handle this with the normal one-character code. */ - mclength = PUTCHAR(c, mcbuffer); goto ONE_CHAR; } /* End of 1-char optimization */ @@ -5899,11 +5898,22 @@ for (;; ptr++) goto FAILED; } cb->had_accept = TRUE; + /* In the first pass, just accumulate the length required; + otherwise hitting (*ACCEPT) inside many nested parentheses can + cause workspace overflow. */ for (oc = cb->open_caps; oc != NULL; oc = oc->next) { - *code++ = OP_CLOSE; - PUT2INC(code, 0, oc->number); + if (lengthptr != NULL) + { + *lengthptr += CU2BYTES(1) + IMM2_SIZE; + } + else + { + *code++ = OP_CLOSE; + PUT2INC(code, 0, oc->number); + } } + setverb = *code++ = (cb->assert_depth > 0)? OP_ASSERT_ACCEPT : OP_ACCEPT; From bf0e91cc97a7430f69d29e4448e36974581df902 Mon Sep 17 00:00:00 2001 From: David Adam Date: Tue, 19 Apr 2016 12:15:08 +0800 Subject: [PATCH 116/363] pcre2: add maintainer mode and disable by default --- pcre2-10.21/Makefile.in | 9 +++++---- pcre2-10.21/aclocal.m4 | 36 ++++++++++++++++++++++++++++++++++++ pcre2-10.21/configure | 39 +++++++++++++++++++++++++++++++++++++++ pcre2-10.21/configure.ac | 6 ++++++ 4 files changed, 86 insertions(+), 4 deletions(-) diff --git a/pcre2-10.21/Makefile.in b/pcre2-10.21/Makefile.in index d86120b5b..fd07c58cb 100644 --- a/pcre2-10.21/Makefile.in +++ b/pcre2-10.21/Makefile.in @@ -714,6 +714,7 @@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ @@ -1215,7 +1216,7 @@ all: $(BUILT_SOURCES) .SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs am--refresh: Makefile @: -$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ @@ -1241,9 +1242,9 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) $(SHELL) ./config.status --recheck -$(top_srcdir)/configure: $(am__configure_deps) +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) $(am__cd) $(srcdir) && $(AUTOCONF) -$(ACLOCAL_M4): $(am__aclocal_m4_deps) +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) $(am__aclocal_m4_deps): @@ -1254,7 +1255,7 @@ src/config.h: src/stamp-h1 src/stamp-h1: $(top_srcdir)/src/config.h.in $(top_builddir)/config.status @rm -f src/stamp-h1 cd $(top_builddir) && $(SHELL) ./config.status src/config.h -$(top_srcdir)/src/config.h.in: $(am__configure_deps) +$(top_srcdir)/src/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) rm -f src/stamp-h1 touch $@ diff --git a/pcre2-10.21/aclocal.m4 b/pcre2-10.21/aclocal.m4 index 10c9cd1c9..795657e89 100644 --- a/pcre2-10.21/aclocal.m4 +++ b/pcre2-10.21/aclocal.m4 @@ -981,6 +981,42 @@ fi rmdir .tst 2>/dev/null AC_SUBST([am__leading_dot])]) +# Add --enable-maintainer-mode option to configure. -*- Autoconf -*- +# From Jim Meyering + +# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_MAINTAINER_MODE([DEFAULT-MODE]) +# ---------------------------------- +# Control maintainer-specific portions of Makefiles. +# Default is to disable them, unless 'enable' is passed literally. +# For symmetry, 'disable' may be passed as well. Anyway, the user +# can override the default with the --enable/--disable switch. +AC_DEFUN([AM_MAINTAINER_MODE], +[m4_case(m4_default([$1], [disable]), + [enable], [m4_define([am_maintainer_other], [disable])], + [disable], [m4_define([am_maintainer_other], [enable])], + [m4_define([am_maintainer_other], [enable]) + m4_warn([syntax], [unexpected argument to AM@&t@_MAINTAINER_MODE: $1])]) +AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) + dnl maintainer-mode's default is 'disable' unless 'enable' is passed + AC_ARG_ENABLE([maintainer-mode], + [AS_HELP_STRING([--]am_maintainer_other[-maintainer-mode], + am_maintainer_other[ make rules and dependencies not useful + (and sometimes confusing) to the casual installer])], + [USE_MAINTAINER_MODE=$enableval], + [USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes])) + AC_MSG_RESULT([$USE_MAINTAINER_MODE]) + AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes]) + MAINT=$MAINTAINER_MODE_TRUE + AC_SUBST([MAINT])dnl +] +) + # Check to see how 'make' treats includes. -*- Autoconf -*- # Copyright (C) 2001-2014 Free Software Foundation, Inc. diff --git a/pcre2-10.21/configure b/pcre2-10.21/configure index e9f9e374d..ad2696549 100755 --- a/pcre2-10.21/configure +++ b/pcre2-10.21/configure @@ -734,6 +734,9 @@ CFLAGS CC ac_ct_AR AR +MAINT +MAINTAINER_MODE_FALSE +MAINTAINER_MODE_TRUE AM_BACKSLASH AM_DEFAULT_VERBOSITY AM_DEFAULT_V @@ -803,6 +806,7 @@ ac_subst_files='' ac_user_opts=' enable_option_checking enable_silent_rules +enable_maintainer_mode enable_dependency_tracking enable_shared enable_static @@ -1482,6 +1486,9 @@ Optional Features: --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-silent-rules less verbose build output (undo: "make V=1") --disable-silent-rules verbose build output (undo: "make V=0") + --enable-maintainer-mode + enable make rules and dependencies not useful (and + sometimes confusing) to the casual installer --enable-dependency-tracking do not reject slow dependency extractors --disable-dependency-tracking @@ -3132,6 +3139,34 @@ AM_BACKSLASH='\' ac_config_headers="$ac_config_headers src/config.h" +# FISH PATCH +# Enable maintainer mode to avoid spurious rebuilds due to timestamps in git +# not being stored. Discussion in https://github.com/fish-shell/fish-shell/issues/2469 + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5 +$as_echo_n "checking whether to enable maintainer-specific portions of Makefiles... " >&6; } + # Check whether --enable-maintainer-mode was given. +if test "${enable_maintainer_mode+set}" = set; then : + enableval=$enable_maintainer_mode; USE_MAINTAINER_MODE=$enableval +else + USE_MAINTAINER_MODE=no +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_MAINTAINER_MODE" >&5 +$as_echo "$USE_MAINTAINER_MODE" >&6; } + if test $USE_MAINTAINER_MODE = yes; then + MAINTAINER_MODE_TRUE= + MAINTAINER_MODE_FALSE='#' +else + MAINTAINER_MODE_TRUE='#' + MAINTAINER_MODE_FALSE= +fi + + MAINT=$MAINTAINER_MODE_TRUE + + +# END FISH PATCH + # This is a new thing required to stop a warning from automake 1.12 DEPDIR="${am__leading_dot}deps" @@ -15990,6 +16025,10 @@ else am__EXEEXT_FALSE= fi +if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then + as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then as_fn_error $? "conditional \"AMDEP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/pcre2-10.21/configure.ac b/pcre2-10.21/configure.ac index 99ad8a9f0..32307644e 100644 --- a/pcre2-10.21/configure.ac +++ b/pcre2-10.21/configure.ac @@ -29,6 +29,12 @@ AM_INIT_AUTOMAKE([dist-bzip2 dist-zip]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) AC_CONFIG_HEADERS(src/config.h) +# FISH PATCH +# Enable maintainer mode to avoid spurious rebuilds due to timestamps in git +# not being stored. Discussion in https://github.com/fish-shell/fish-shell/issues/2469 +AM_MAINTAINER_MODE +# END FISH PATCH + # This is a new thing required to stop a warning from automake 1.12 m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) From 43535cf5a5a295f7a34adede28afd90dcd9aa739 Mon Sep 17 00:00:00 2001 From: David Adam Date: Tue, 19 Apr 2016 15:55:30 +0800 Subject: [PATCH 117/363] Bump version for 2.3b1 --- CHANGELOG.md | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++ osx/Info.plist | 4 ++-- osx/config.h | 4 ++-- 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa0b82941..702a2ad52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,57 @@ +# fish 2.3b1 + +## Significant Changes + +- A new `string` builtin to handle... strings! (#2296) +- Allow using escape as the Meta modifier key, by waiting after seeing an escape character wait up to 300ms for an additional character. This is consistent with readline (e.g. bash) and can be configured via the fish_escape_delay_ms variable. This allows using escape as the Meta modifier. (#1356) +- Add new directories for vendor functions and configuration snippets (#2500) + +# Backward-incompatible changes + +- Unmatched globs will now cause an error, except when used with `for`, `set` or `count` (#2719) +- `and` and `or` will now bind to the closest `if` or `while`, allowing compound conditions without `begin` and `end` (#1428) +- `set -ql` now searches up to function scope for variables (#2502) +- `status -f` will now behave the same when run as the main script or using `source` (#2643) +- `source` no longer puts the file name in `$argv` if no arguments are given (#139) + +# Other notable fixes and improvements + +- Fish no longer silences errors in config.fish (#2702) +- Move the history file to $XDG_DATA_HOME/fish (or ~/.local/share if it has not been set) +- Directory autosuggestions will now descend as far as possible if there is only one child directory (#2531) +- Add support for bright colors (#1464) +- Allow Ctrl-J (\cj) to be bound separately from Ctrl-M (\cm) (#217) +- psub now has a "-s"/"–suffix" option to name the temporary file with that suffix +- Enable 24-bit colors on select terminals (#2495) +- Support for SVN status in the prompt (#2582) +- Mercurial and SVN support have been added to the Classic + Git (now Classic + VCS) prompt (via the new \__fish_vcs_prompt function) (#2592) +- export now handles variables with a "=" in the value (#2403) +- New completions for: + - alsactl + - Archlinux's asp, makepkg + - Atom's apm (#2390) + - entr - the "Event Notify Test Runner" (#2265) + - Fedora's dnf (#2638) + - OSX diskutil (#2738) + - pkgng (#2395) + - pulseaudio's pacmd and pactl + - rust's rustc and cargo (#2409) + - sysctl (#2214) + - systemd's machinectl (#2158), busctl (#2144), systemd-nspawn, systemd-analyze, localectl, timedatectl + - and more +- Fish no longer has a function called sgrep, freeing it for user customization (#2245) +- A rewrite of the completions for cd, fixing a few bugs (#2299, #2300, #562) +- Linux VTs now run in a simplified mode to avoid issues (#2311) +- The vi-bindings now inherit from the emacs bindings +- Fish will also execute fish_user_key_bindings when in vi-mode +- `funced` will now also check $VISUAL (#2268) +- A new `suspend` function (#2269) +- Subcommand completion now works better with split /usr (#2141) +- The command-not-found-handler can now be overridden by defining a function called __fish_command_not_found_handler in config.fish (#2332) +- A few fixes to the Sorin theme +- PWD shortening in the prompt can now be configured via the fish_prompt_pwd_dir_length variable, set to the length per path component (#2473) +- fish now ships a skeleton file for /etc/fish/config.fish that only contains some documentation, the included code has been moved to the corresponding file in /usr (#2799) + # fish 2.2.0 (released July 12, 2015) ### Significant changes ### diff --git a/osx/Info.plist b/osx/Info.plist index 68fdbe1f9..899ed3d58 100644 --- a/osx/Info.plist +++ b/osx/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 2.0 + 2.2.990 CFBundleVersion - 289.5 + 0.1 LSApplicationCategoryType public.app-category.productivity LSMinimumSystemVersion diff --git a/osx/config.h b/osx/config.h index 0a5530168..202567aae 100644 --- a/osx/config.h +++ b/osx/config.h @@ -222,7 +222,7 @@ #define PACKAGE_NAME "fish" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "fish 2.2.0-git" +#define PACKAGE_STRING "fish 2.3b1" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "fish" @@ -231,7 +231,7 @@ #define PACKAGE_URL "" /* Define to the version of this package. */ -#define PACKAGE_VERSION "2.2.0-git" +#define PACKAGE_VERSION "2.3b1" /* The size of `wchar_t', as computed by sizeof. */ #define SIZEOF_WCHAR_T 4 From 0f888ad4e6d85303230507d147ee6e1170e861aa Mon Sep 17 00:00:00 2001 From: David Adam Date: Tue, 19 Apr 2016 22:12:53 +0800 Subject: [PATCH 118/363] CHANGELOG: flesh out some of the bigger changes in 2.3 [ci skip] --- CHANGELOG.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 702a2ad52..f323ff6dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,9 @@ -# fish 2.3b1 +# fish 2.3b1 (released April 19, 2016) ## Significant Changes -- A new `string` builtin to handle... strings! (#2296) -- Allow using escape as the Meta modifier key, by waiting after seeing an escape character wait up to 300ms for an additional character. This is consistent with readline (e.g. bash) and can be configured via the fish_escape_delay_ms variable. This allows using escape as the Meta modifier. (#1356) +- A new `string` builtin to handle... strings! This builtin will measure, split, search and replace text strings, including using regular expressions. It can also be used to turn lists into plain strings using `join`. `string` can be used in place of `sed`, `grep`, `tr`, `cut`, and `awk` in many situations. (#2296) +- Allow using escape as the Meta modifier key, by waiting after seeing an escape character wait up to 300ms for an additional character. This is consistent with readline (e.g. bash) and can be configured via the `fish_escape_delay_ms variable`. This allows using escape as the Meta modifier. (#1356) - Add new directories for vendor functions and configuration snippets (#2500) # Backward-incompatible changes @@ -13,11 +13,11 @@ - `set -ql` now searches up to function scope for variables (#2502) - `status -f` will now behave the same when run as the main script or using `source` (#2643) - `source` no longer puts the file name in `$argv` if no arguments are given (#139) +- History files are stored under the `XDG_DATA_HOME` hierarchy (by default, in `~/.local/share`), and existing history will be moved on first use (#744) # Other notable fixes and improvements - Fish no longer silences errors in config.fish (#2702) -- Move the history file to $XDG_DATA_HOME/fish (or ~/.local/share if it has not been set) - Directory autosuggestions will now descend as far as possible if there is only one child directory (#2531) - Add support for bright colors (#1464) - Allow Ctrl-J (\cj) to be bound separately from Ctrl-M (\cm) (#217) @@ -43,14 +43,14 @@ - A rewrite of the completions for cd, fixing a few bugs (#2299, #2300, #562) - Linux VTs now run in a simplified mode to avoid issues (#2311) - The vi-bindings now inherit from the emacs bindings -- Fish will also execute fish_user_key_bindings when in vi-mode +- Fish will also execute `fish_user_key_bindings` when in vi-mode - `funced` will now also check $VISUAL (#2268) - A new `suspend` function (#2269) - Subcommand completion now works better with split /usr (#2141) -- The command-not-found-handler can now be overridden by defining a function called __fish_command_not_found_handler in config.fish (#2332) +- The command-not-found-handler can now be overridden by defining a function called `__fish_command_not_found_handler` in config.fish (#2332) - A few fixes to the Sorin theme -- PWD shortening in the prompt can now be configured via the fish_prompt_pwd_dir_length variable, set to the length per path component (#2473) -- fish now ships a skeleton file for /etc/fish/config.fish that only contains some documentation, the included code has been moved to the corresponding file in /usr (#2799) +- PWD shortening in the prompt can now be configured via the `fish_prompt_pwd_dir_length` variable, set to the length per path component (#2473) +- fish no longer requires `/etc/fish/config.fish` to correctly start, and now ships a skeleton file that only contains some documentation (#2799) # fish 2.2.0 (released July 12, 2015) From bd4622a0d01c7d882c37c46f6730567750dda657 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Tue, 19 Apr 2016 18:17:39 -0700 Subject: [PATCH 119/363] make comments Xcode friendly The OS X Xcode IDE has a weird requirement that block comments preceding a function or class definition must begin with three slashes rather than two if you want the comment displayed in the "Quick Help" window. --- CONTRIBUTING.md | 2 + src/autoload.cpp | 24 ++++---- src/builtin.cpp | 146 +++++++++++++++++++++++------------------------ src/history.cpp | 102 ++++++++++++++++----------------- 4 files changed, 137 insertions(+), 137 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e73bc3446..8e610b130 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -84,6 +84,8 @@ The first word of global variable names should generally be `fish` for public va 1. Comments should always use the C++ style; i.e., each line of the comment should begin with a `//` and should be limited to 100 characters. Comments that do not begin a line should be separated from the previous text by two spaces. +1. Comments that document the purpose of a function or class should begin with three slashes, `///`, so that OS X Xcode (and possibly other ideas) will extract the comment and show it in the "Quick Help" window when the cursor is on the symbol. + ## Testing The source code for fish includes a large collection of tests. If you are making any changes to fish, running these tests is highly recommended to make sure the behaviour remains consistent. diff --git a/src/autoload.cpp b/src/autoload.cpp index 1c0817349..ae63335aa 100644 --- a/src/autoload.cpp +++ b/src/autoload.cpp @@ -122,7 +122,7 @@ static bool script_name_precedes_script_name(const builtin_script_t &script1, return wcscmp(script1.name, script2.name) < 0; } -// Check whether the given command is loaded. +/// Check whether the given command is loaded. bool autoload_t::has_tried_loading(const wcstring &cmd) { scoped_lock locker(lock); autoload_function_t *func = this->get_node(cmd); @@ -150,17 +150,17 @@ autoload_function_t *autoload_t::get_autoloaded_function_with_creation(const wcs return func; } -// This internal helper function does all the real work. By using two functions, the internal -// function can return on various places in the code, and the caller can take care of various -// cleanup work. -// -// cmd: the command name ('grep') -// really_load: whether to actually parse it as a function, or just check it it exists -// reload: whether to reload it if it's already loaded -// path_list: the set of paths to check -// -// Result: if really_load is true, returns whether the function was loaded. Otherwise returns -// whether the function existed. +/// This internal helper function does all the real work. By using two functions, the internal +/// function can return on various places in the code, and the caller can take care of various +/// cleanup work. +/// +/// cmd: the command name ('grep') +/// really_load: whether to actually parse it as a function, or just check it it exists +/// reload: whether to reload it if it's already loaded +/// path_list: the set of paths to check +/// +/// Result: if really_load is true, returns whether the function was loaded. Otherwise returns +/// whether the function existed. bool autoload_t::locate_file_and_maybe_load_it(const wcstring &cmd, bool really_load, bool reload, const wcstring_list_t &path_list) { // Note that we are NOT locked in this function! diff --git a/src/builtin.cpp b/src/builtin.cpp index 87b69b6c2..a2bf6736e 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -71,7 +71,7 @@ // The send stuff to foreground message. #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; @@ -92,7 +92,7 @@ bool builtin_data_t::operator<(const builtin_data_t *other) const { return wcscmp(this->name, other->name) < 0; } -// Counts the number of non null pointers in the specified array. +/// Counts the number of non null pointers in the specified array. int builtin_count_args(const wchar_t *const *argv) { int argc = 1; while (argv[argc] != NULL) { @@ -101,8 +101,8 @@ int builtin_count_args(const wchar_t *const *argv) { return argc; } -// This function works like wperror, but it prints its result into the streams.err string instead of -// to stderr. Used by the builtin commands. +/// This function works like wperror, but it prints its result into the streams.err string instead +/// to stderr. Used by the builtin commands. static void builtin_wperror(const wchar_t *s, io_streams_t &streams) { char *err = strerror(errno); if (s != NULL) { @@ -116,7 +116,7 @@ static void builtin_wperror(const wchar_t *s, io_streams_t &streams) { } } -// Count the number of times the specified character occurs in the specified string. +/// Count the number of times the specified character occurs in the specified string. static int count_char(const wchar_t *str, wchar_t c) { int res = 0; for (; *str; str++) { @@ -148,10 +148,10 @@ wcstring builtin_help_get(parser_t &parser, io_streams_t &streams, const wchar_t return out; } -// Print help for the specified builtin. If \c b is sb_err, also print the line information. -// -// If \c b is the buffer representing standard error, and the help message is about to be printed to -// an interactive screen, it may be shortened to fit the screen. +/// Print help for the specified builtin. If \c b is sb_err, also print the line information. +/// +/// If \c b is the buffer representing standard error, and the help message is about to be printed +/// to an interactive screen, it may be shortened to fit the screen. void builtin_print_help(parser_t &parser, io_streams_t &streams, const wchar_t *cmd, output_stream_t &b) { bool is_stderr = (&b == &streams.err); @@ -227,14 +227,14 @@ void builtin_print_help(parser_t &parser, io_streams_t &streams, const wchar_t * } } -// Perform error reporting for encounter with unknown option. +/// Perform error reporting for encounter with unknown option. static void builtin_unknown_option(parser_t &parser, io_streams_t &streams, const wchar_t *cmd, const wchar_t *opt) { streams.err.append_format(BUILTIN_ERR_UNKNOWN, cmd, opt); builtin_print_help(parser, streams, cmd, streams.err); } -// Perform error reporting for encounter with missing argument. +/// Perform error reporting for encounter with missing argument. static void builtin_missing_argument(parser_t &parser, io_streams_t &streams, const wchar_t *cmd, const wchar_t *opt) { streams.err.append_format(BUILTIN_ERR_MISSING, cmd, opt); @@ -269,8 +269,8 @@ int builtin_test(parser_t &parser, io_streams_t &streams, wchar_t **argv); // builtin_string lives in builtin_string.cpp int builtin_string(parser_t &parser, io_streams_t &streams, wchar_t **argv); -// List a single key binding. -// Returns false if no binding with that sequence and mode exists. +/// List a single key binding. +/// Returns false if no binding with that sequence and mode exists. static bool builtin_bind_list_one(const wcstring &seq, const wcstring &bind_mode, io_streams_t &streams) { std::vector ecmds; @@ -317,7 +317,7 @@ static bool builtin_bind_list_one(const wcstring &seq, const wcstring &bind_mode return true; } -// List all current key bindings. +/// List all current key bindings. static void builtin_bind_list(const wchar_t *bind_mode, io_streams_t &streams) { const std::vector lst = input_mapping_get_names(); @@ -331,10 +331,10 @@ static void builtin_bind_list(const wchar_t *bind_mode, io_streams_t &streams) { } } -// Print terminfo key binding names to string buffer used for standard output. -// -// \param all if set, all terminfo key binding names will be printed. If not set, only ones that -// are defined for this terminal are printed. +/// Print terminfo key binding names to string buffer used for standard output. +/// +/// \param all if set, all terminfo key binding names will be printed. If not set, only ones that +/// are defined for this terminal are printed. static void builtin_bind_key_names(int all, io_streams_t &streams) { const wcstring_list_t names = input_terminfo_get_names(!all); for (size_t i = 0; i < names.size(); i++) { @@ -344,7 +344,7 @@ static void builtin_bind_key_names(int all, io_streams_t &streams) { } } -// Print all the special key binding functions to string buffer used for standard output. +/// Print all the special key binding functions to string buffer used for standard output. static void builtin_bind_function_names(io_streams_t &streams) { wcstring_list_t names = input_function_get_names(); @@ -354,7 +354,7 @@ static void builtin_bind_function_names(io_streams_t &streams) { } } -// Wraps input_terminfo_get_sequence(), appending the correct error messages as needed. +/// Wraps input_terminfo_get_sequence(), appending the correct error messages as needed. static int get_terminfo_sequence(const wchar_t *seq, wcstring *out_seq, io_streams_t &streams) { if (input_terminfo_get_sequence(seq, out_seq)) { return 1; @@ -380,7 +380,7 @@ static int get_terminfo_sequence(const wchar_t *seq, wcstring *out_seq, io_strea return 0; } -// Add specified key binding. +/// Add specified key binding. static int builtin_bind_add(const wchar_t *seq, const wchar_t *const *cmds, size_t cmds_len, const wchar_t *mode, const wchar_t *sets_mode, int terminfo, io_streams_t &streams) { @@ -399,12 +399,12 @@ static int builtin_bind_add(const wchar_t *seq, const wchar_t *const *cmds, size return 0; } -// Erase specified key bindings -// -// \param seq an array of all key bindings to erase -// \param all if specified, _all_ key bindings will be erased -// \param mode if specified, only bindings from that mode will be erased. If not given and \c all is -// \c false, \c DEFAULT_BIND_MODE will be used. +/// Erase specified key bindings +/// +/// \param seq an array of all key bindings to erase +/// \param all if specified, _all_ key bindings will be erased +/// \param mode if specified, only bindings from that mode will be erased. If not given and \c all +/// is \c false, \c DEFAULT_BIND_MODE will be used. static int builtin_bind_erase(wchar_t **seq, int all, const wchar_t *mode, int use_terminfo, io_streams_t &streams) { if (all) { @@ -439,7 +439,7 @@ static int builtin_bind_erase(wchar_t **seq, int all, const wchar_t *mode, int u } } -// The bind builtin, used for setting character sequences. +/// The bind builtin, used for setting character sequences. static int builtin_bind(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wgetopter_t w; enum { BIND_INSERT, BIND_ERASE, BIND_KEY_NAMES, BIND_FUNCTION_NAMES }; @@ -594,7 +594,7 @@ static int builtin_bind(parser_t &parser, io_streams_t &streams, wchar_t **argv) return res; } -// The block builtin, used for temporarily blocking events. +/// The block builtin, used for temporarily blocking events. static int builtin_block(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wgetopter_t w; enum { @@ -699,9 +699,9 @@ static int builtin_block(parser_t &parser, io_streams_t &streams, wchar_t **argv return STATUS_BUILTIN_OK; } -// The builtin builtin, used for giving builtins precedence over functions. Mostly handled by the -// parser. All this code does is some additional operational modes, such as printing a list of all -// builtins, printing help, etc. +/// The builtin builtin, used for giving builtins precedence over functions. Mostly handled by the +/// parser. All this code does is some additional operational modes, such as printing a list of all +/// builtins, printing help, etc. static int builtin_builtin(parser_t &parser, io_streams_t &streams, wchar_t **argv) { int argc = builtin_count_args(argv); int list = 0; @@ -753,7 +753,7 @@ static int builtin_builtin(parser_t &parser, io_streams_t &streams, wchar_t **ar return STATUS_BUILTIN_OK; } -// Implementation of the builtin emit command, used to create events. +/// Implementation of the builtin emit command, used to create events. static int builtin_emit(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wgetopter_t w; int argc = builtin_count_args(argv); @@ -796,8 +796,8 @@ static int builtin_emit(parser_t &parser, io_streams_t &streams, wchar_t **argv) return STATUS_BUILTIN_OK; } -// Implementation of the builtin 'command'. Actual command running is handled by the parser, this -// just processes the flags. +/// Implementation of the builtin 'command'. Actual command running is handled by the parser, this +/// just processes the flags. static int builtin_command(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wgetopter_t w; int argc = builtin_count_args(argv); @@ -856,8 +856,8 @@ static int builtin_command(parser_t &parser, io_streams_t &streams, wchar_t **ar return found ? STATUS_BUILTIN_OK : STATUS_BUILTIN_ERROR; } -// A generic bultin that only supports showing a help message. This is only a placeholder that -// prints the help message. Useful for commands that live in the parser. +/// A generic bultin that only supports showing a help message. This is only a placeholder that +/// prints the help message. Useful for commands that live in the parser. static int builtin_generic(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wgetopter_t w; int argc = builtin_count_args(argv); @@ -898,7 +898,7 @@ static int builtin_generic(parser_t &parser, io_streams_t &streams, wchar_t **ar return STATUS_BUILTIN_ERROR; } -// Return a definition of the specified function. Used by the functions builtin. +/// Return a definition of the specified function. Used by the functions builtin. static wcstring functions_def(const wcstring &name) { CHECK(!name.empty(), L""); wcstring out; @@ -1004,7 +1004,7 @@ static wcstring functions_def(const wcstring &name) { return out; } -// The functions builtin, used for listing and erasing functions. +/// The functions builtin, used for listing and erasing functions. static int builtin_functions(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wgetopter_t w; int i; @@ -1252,12 +1252,12 @@ static unsigned int builtin_echo_digit(wchar_t wc, unsigned int base) { return UINT_MAX; } -// Parse a numeric escape sequence in str, returning whether we succeeded. Also return the number of -// characters consumed and the resulting value. Supported escape sequences: -// -// \0nnn: octal value, zero to three digits -// \nnn: octal value, one to three digits -// \xhh: hex value, one to two digits +/// Parse a numeric escape sequence in str, returning whether we succeeded. Also return the number +/// of characters consumed and the resulting value. Supported escape sequences: +/// +/// \0nnn: octal value, zero to three digits +/// \nnn: octal value, one to three digits +/// \xhh: hex value, one to two digits static bool builtin_echo_parse_numeric_sequence(const wchar_t *str, size_t *consumed, unsigned char *out_val) { bool success = false; @@ -1299,10 +1299,10 @@ static bool builtin_echo_parse_numeric_sequence(const wchar_t *str, size_t *cons return success; } -// The echo builtin. -// -// Bash only respects -n if it's the first argument. We'll do the same. We also support a new option -// -s to mean "no spaces" +/// The echo builtin. +/// +/// Bash only respects -n if it's the first argument. We'll do the same. We also support a new +/// option -s to mean "no spaces" static int builtin_echo(parser_t &parser, io_streams_t &streams, wchar_t **argv) { /* Skip first arg */ if (!*argv++) return STATUS_BUILTIN_ERROR; @@ -1453,8 +1453,8 @@ static int builtin_echo(parser_t &parser, io_streams_t &streams, wchar_t **argv) return STATUS_BUILTIN_OK; } -// The pwd builtin. We don't respect -P to resolve symbolic links because we -// try to always resolve them. +/// 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, io_streams_t &streams, wchar_t **argv) { wcstring res = wgetcwd(); if (res.empty()) { @@ -1466,7 +1466,7 @@ static int builtin_pwd(parser_t &parser, io_streams_t &streams, wchar_t **argv) } } -// Adds a function to the function set. It calls into function.cpp to perform any heavy lifting. +/// Adds a function to the function set. It calls into function.cpp to perform any heavy lifting. int define_function(parser_t &parser, io_streams_t &streams, const wcstring_list_t &c_args, const wcstring &contents, int definition_line_offset, wcstring *out_err) { wgetopter_t w; @@ -1811,7 +1811,7 @@ static int builtin_random(parser_t &parser, io_streams_t &streams, wchar_t **arg return STATUS_BUILTIN_OK; } -// The read builtin. Reads from stdin and stores the values in environment variables. +/// The read builtin. Reads from stdin and stores the values in environment variables. static int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wgetopter_t w; wcstring buff; @@ -2134,7 +2134,7 @@ static int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) return exit_res; } -// The status builtin. Gives various status information on fish. +/// The status builtin. Gives various status information on fish. static int builtin_status(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wgetopter_t w; enum { @@ -2304,7 +2304,7 @@ static int builtin_status(parser_t &parser, io_streams_t &streams, wchar_t **arg return res; } -// The exit builtin. Calls reader_exit to exit and returns the value specified. +/// The exit builtin. Calls reader_exit to exit and returns the value specified. static int builtin_exit(parser_t &parser, io_streams_t &streams, wchar_t **argv) { int argc = builtin_count_args(argv); @@ -2337,8 +2337,8 @@ static int builtin_exit(parser_t &parser, io_streams_t &streams, wchar_t **argv) return (int)ec; } -// The cd builtin. Changes the current directory to the one specified or to $HOME if none is -// specified. The directory can be relative to any directory in the CDPATH variable. +/// The cd builtin. Changes the current directory to the one specified or to $HOME if none is +/// specified. The directory can be relative to any directory in the CDPATH variable. static int builtin_cd(parser_t &parser, io_streams_t &streams, wchar_t **argv) { env_var_t dir_in; wcstring dir; @@ -2404,7 +2404,7 @@ static int builtin_cd(parser_t &parser, io_streams_t &streams, wchar_t **argv) { return res; } -// Implementation of the builtin count command, used to count the number of arguments sent to it. +/// Implementation of the builtin count command, used to count the number of arguments sent to it. static int builtin_count(parser_t &parser, io_streams_t &streams, wchar_t **argv) { int argc; argc = builtin_count_args(argv); @@ -2412,8 +2412,8 @@ static int builtin_count(parser_t &parser, io_streams_t &streams, wchar_t **argv return !(argc - 1); } -// Implementation of the builtin contains command, used to check if a specified string is part of a -// list. +/// Implementation of the builtin contains command, used to check if a specified string is part of +/// a list. static int builtin_contains(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wgetopter_t w; int argc; @@ -2473,9 +2473,7 @@ static int builtin_contains(parser_t &parser, io_streams_t &streams, wchar_t **a return 1; } -/** - The . (dot) builtin, sometimes called source. Evaluates the contents of a file. -*/ +/// The . (dot) builtin, sometimes called source. Evaluates the contents of a file. static int builtin_source(parser_t &parser, io_streams_t &streams, wchar_t **argv) { ASSERT_IS_MAIN_THREAD(); int fd; @@ -2539,11 +2537,11 @@ static int builtin_source(parser_t &parser, io_streams_t &streams, wchar_t **arg return res; } -// Make the specified job the first job of the job list. Moving jobs around in the list makes the -// list reflect the order in which the jobs were used. +/// Make the specified job the first job of the job list. Moving jobs around in the list makes the +/// list reflect the order in which the jobs were used. static void make_first(job_t *j) { job_promote(j); } -// Builtin for putting a job in the foreground. +/// Builtin for putting a job in the foreground. static int builtin_fg(parser_t &parser, io_streams_t &streams, wchar_t **argv) { job_t *j = NULL; @@ -2632,7 +2630,7 @@ static int builtin_fg(parser_t &parser, io_streams_t &streams, wchar_t **argv) { return j != 0; } -// Helper function for builtin_bg(). +/// Helper function for builtin_bg(). static int send_to_bg(parser_t &parser, io_streams_t &streams, job_t *j, const wchar_t *name) { if (j == 0) { streams.err.append_format(_(L"%ls: Unknown job '%ls'\n"), L"bg", name); @@ -2654,7 +2652,7 @@ static int send_to_bg(parser_t &parser, io_streams_t &streams, job_t *j, const w return STATUS_BUILTIN_OK; } -// Builtin for putting a job in the background. +/// Builtin for putting a job in the background. static int builtin_bg(parser_t &parser, io_streams_t &streams, wchar_t **argv) { int res = STATUS_BUILTIN_OK; @@ -2700,8 +2698,8 @@ static int builtin_bg(parser_t &parser, io_streams_t &streams, wchar_t **argv) { return res; } -// This function handles both the 'continue' and the 'break' builtins that are used for loop -// control. +/// This function handles both the 'continue' and the 'break' builtins that are used for loop +/// control. static int builtin_break_continue(parser_t &parser, io_streams_t &streams, wchar_t **argv) { int is_break = (wcscmp(argv[0], L"break") == 0); int argc = builtin_count_args(argv); @@ -2740,7 +2738,7 @@ static int builtin_break_continue(parser_t &parser, io_streams_t &streams, wchar return STATUS_BUILTIN_OK; } -// Implementation of the builtin breakpoint command, used to launch the interactive debugger. +/// Implementation of the builtin breakpoint command, used to launch the interactive debugger. static int builtin_breakpoint(parser_t &parser, io_streams_t &streams, wchar_t **argv) { parser.push_block(new breakpoint_block_t()); @@ -2751,7 +2749,7 @@ static int builtin_breakpoint(parser_t &parser, io_streams_t &streams, wchar_t * return proc_get_last_status(); } -// Function for handling the \c return builtin. +/// Function for handling the \c return builtin. static int builtin_return(parser_t &parser, io_streams_t &streams, wchar_t **argv) { int argc = builtin_count_args(argv); int status = proc_get_last_status(); @@ -2801,7 +2799,7 @@ static int builtin_return(parser_t &parser, io_streams_t &streams, wchar_t **arg return status; } -// History of commands executed by user. +/// History of commands executed by user. static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **argv) { int argc = builtin_count_args(argv); @@ -3079,7 +3077,7 @@ void builtin_destroy() {} int builtin_exists(const wcstring &cmd) { return !!builtin_lookup(cmd); } -// Return true if the specified builtin should handle it's own help, false otherwise. +/// Return true if the specified builtin should handle it's own help, false otherwise. 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", diff --git a/src/history.cpp b/src/history.cpp index af5d062ef..bc9ddd3ed 100644 --- a/src/history.cpp +++ b/src/history.cpp @@ -51,9 +51,9 @@ namespace { -// Helper class for certain output. This is basically a string that allows us to ensure we only -// flush at record boundaries, and avoids the copying of ostringstream. Have you ever tried to -// implement your own streambuf? Total insanity. +/// Helper class for certain output. This is basically a string that allows us to ensure we only +/// flush at record boundaries, and avoids the copying of ostringstream. Have you ever tried to +/// implement your own streambuf? Total insanity. class history_output_buffer_t { // A null-terminated C string. std::vector buffer; @@ -64,10 +64,10 @@ class history_output_buffer_t { static size_t safe_strlen(const char *s) { return s ? strlen(s) : 0; } public: - // Add a bit more to HISTORY_OUTPUT_BUFFER_SIZE because we flush once we've exceeded that size. + /// Add a bit more to HISTORY_OUTPUT_BUFFER_SIZE because we flush once we've exceeded that size. history_output_buffer_t() : buffer(HISTORY_OUTPUT_BUFFER_SIZE + 128, '\0'), offset(0) {} - // Append one or more strings. + /// Append one or more strings. void append(const char *s1, const char *s2 = NULL, const char *s3 = NULL) { const char *ptrs[4] = {s1, s2, s3, NULL}; const size_t lengths[4] = {safe_strlen(s1), safe_strlen(s2), safe_strlen(s3), 0}; @@ -95,14 +95,14 @@ class history_output_buffer_t { assert(buffer.at(buffer.size() - 1) == '\0'); } - // Output to a given fd, resetting our buffer. Returns true on success, false on error. + /// Output to a given fd, resetting our buffer. Returns true on success, false on error. bool flush_to_fd(int fd) { bool result = write_loop(fd, &buffer.at(0), offset) >= 0; offset = 0; return result; } - // Return how much data we've accumulated. + /// Return how much data we've accumulated. size_t output_size() const { return offset; } }; @@ -126,7 +126,7 @@ class time_profiler_t { } }; -// Lock a file via fcntl; returns true on success, false on failure. +/// Lock a file via fcntl; returns true on success, false on failure. static bool history_file_lock(int fd, short type) { assert(type == F_RDLCK || type == F_WRLCK); struct flock flk = {}; @@ -136,8 +136,8 @@ static bool history_file_lock(int fd, short type) { return ret != -1; } -// Our LRU cache is used for restricting the amount of history we have, and limiting how long we -// order it. +/// 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: time_t timestamp; @@ -150,13 +150,13 @@ class history_lru_node_t : public lru_node_t { class history_lru_cache_t : public lru_cache_t { protected: - // Override to delete evicted nodes. + /// Override to delete evicted nodes. virtual void node_was_evicted(history_lru_node_t *node) { delete node; } public: explicit history_lru_cache_t(size_t max) : lru_cache_t(max) {} - // Function to add a history item. + /// Function to add a history item. void add_item(const history_item_t &item) { // Skip empty items. if (item.empty()) return; @@ -197,15 +197,15 @@ static history_collection_t histories; static wcstring history_filename(const wcstring &name, const wcstring &suffix); -// Replaces newlines with a literal backslash followed by an n, and replaces backslashes with two -// backslashes. +/// Replaces newlines with a literal backslash followed by an n, and replaces backslashes with two +/// backslashes. static void escape_yaml(std::string *str); -// Inverse of escape_yaml. +/// Inverse of escape_yaml. static void unescape_yaml(std::string *str); -// We can merge two items if they are the same command. We use the more recent timestamp, more -// recent identifier, and the longer list of required paths. +/// We can merge two items if they are the same command. We use the more recent timestamp, more +/// recent identifier, and the longer list of required paths. bool history_item_t::merge(const history_item_t &item) { bool result = false; if (this->contents == item.contents) { @@ -246,7 +246,7 @@ bool history_item_t::matches_search(const wcstring &term, enum history_search_ty } } -// Append our YAML history format to the provided vector at the given offset, updating the offset. +/// Append our YAML history format to the provided vector at the given offset, updating the offset. static void append_yaml_to_buffer(const wcstring &wcmd, time_t timestamp, const path_list_t &required_paths, history_output_buffer_t *buffer) { @@ -270,9 +270,9 @@ static void append_yaml_to_buffer(const wcstring &wcmd, time_t timestamp, } } -// 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 +/// 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) { const char *cursor = str; // Advance past spaces. @@ -295,8 +295,8 @@ static bool parse_timestamp(const char *str, time_t *out_when) { return false; } -// 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. +/// 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) { // Handle the hopeless case. if (length < 1) return NULL; @@ -323,11 +323,11 @@ static const char *next_line(const char *start, size_t length) { return nextline; } -// Support for iteratively locating the offsets of history items. -// Pass the address and length of a mapped region. -// Pass a pointer to a cursor size_t, initially 0. -// If custoff_timestamp is nonzero, skip items created at or after that timestamp. -// Returns (size_t)(-1) when done. +/// Support for iteratively locating the offsets of history items. +/// Pass the address and length of a mapped region. +/// Pass a pointer to a cursor size_t, initially 0. +/// If custoff_timestamp is nonzero, skip items created at or after that timestamp. +/// Returns (size_t)(-1) when done. static size_t offset_of_next_item_fish_2_0(const char *begin, size_t mmap_length, size_t *inout_cursor, time_t cutoff_timestamp) { size_t cursor = *inout_cursor; @@ -415,8 +415,8 @@ static size_t offset_of_next_item_fish_2_0(const char *begin, size_t mmap_length return result; } -// 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 +/// 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) { if (mmap_length == 0 || *inout_cursor >= mmap_length) return (size_t)(-1); @@ -456,7 +456,7 @@ static size_t offset_of_next_item_fish_1_x(const char *begin, size_t mmap_length return result; } -// Returns the offset of the next item based on the given history type, or -1. +/// 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) { @@ -695,8 +695,8 @@ history_item_t history_t::item_at_index(size_t idx) { return history_item_t(wcstring(), 0); } -// 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. +/// 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) { // Locate the newline. assert(cursor <= len); @@ -713,7 +713,7 @@ 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. +/// Trims leading spaces in the given string, returning how many there were. static size_t trim_leading_spaces(std::string &str) { size_t i = 0, max = str.size(); while (i < max && str[i] == ' ') i++; @@ -738,7 +738,7 @@ static bool extract_prefix_and_unescape_yaml(std::string *key, std::string *valu return where != std::string::npos; } -// Decode an item via the fish 2.0 format. +/// 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) { wcstring cmd; time_t when = 0; @@ -812,8 +812,8 @@ 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. +/// 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) { wcstring out; for (const wchar_t *in = in_str.c_str(); *in; in++) { @@ -828,7 +828,7 @@ static wcstring history_unescape_newlines_fish_1_x(const wcstring &in_str) { return out; } -// Decode an item via the fish 1.x format. Adapted from fish 1.x's item_get(). +/// 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) { const char *end = begin + length; const char *pos = begin; @@ -901,7 +901,7 @@ history_item_t history_t::decode_item_fish_1_x(const char *begin, size_t length) return history_item_t(out, timestamp); } -// Try to infer the history file type based on inspecting the data. +/// 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) { history_file_type_t result = history_type_unknown; if (len > 0) { // old fish started with a # @@ -928,8 +928,8 @@ void history_t::populate_from_mmap(void) { } } -// Do a private, read-only map of the entirety of a history file with the given name. Returns true -// if successful. Returns the mapped memory region by reference. +/// Do a private, read-only map of the entirety of a history file with the given name. Returns true +/// if successful. Returns the mapped memory region by reference. bool history_t::map_file(const wcstring &name, const char **out_map_start, size_t *out_map_len, file_id_t *file_id) { bool result = false; @@ -1042,13 +1042,13 @@ bool history_search_t::go_backwards() { return false; } -// Goes to the end (forwards). +/// Goes to the end (forwards). void history_search_t::go_to_end(void) { prev_matches.clear(); } -// Returns if we are at the end, which is where we start. +/// Returns if we are at the end, which is where we start. bool history_search_t::is_at_end(void) const { return prev_matches.empty(); } -// Goes to the beginning (backwards). +/// Goes to the beginning (backwards). void history_search_t::go_to_beginning(void) { // Go backwards as far as we can. while (go_backwards()) @@ -1087,7 +1087,7 @@ static void escape_yaml(std::string *str) { replace_all(str, "\n", "\\n"); // replace newline with backslash + literal n } -// This function is called frequently, so it ought to be fast. +/// This function is called frequently, so it ought to be fast. static void unescape_yaml(std::string *str) { size_t cursor = 0, size = str->size(); while (cursor < size) { @@ -1397,7 +1397,7 @@ bool history_t::save_internal_via_appending() { return ok; } -// Save the specified mode to file; optionally also vacuums. +/// Save the specified mode to file; optionally also vacuums. void history_t::save_internal(bool vacuum) { ASSERT_IS_LOCKED(lock); @@ -1475,9 +1475,9 @@ bool history_t::is_empty(void) { return empty; } -// Populates from older location (in config path, rather than data path) This is accomplished by -// clearing ourselves, and copying the contents of the old history file to the new history file. -// The new contents will automatically be re-mapped later. +/// Populates from older location (in config path, rather than data path) This is accomplished by +/// clearing ourselves, and copying the contents of the old history file to the new history file. +/// The new contents will automatically be re-mapped later. void history_t::populate_from_config_path() { wcstring old_file; if (path_get_config(old_file)) { @@ -1510,7 +1510,7 @@ void history_t::populate_from_config_path() { } } -// Indicate whether we ought to import the bash history file into fish. +/// Indicate whether we ought to import the bash history file into fish. static bool should_import_bash_history_line(const std::string &line) { if (line.empty()) return false; @@ -1717,7 +1717,7 @@ void history_t::add_pending_with_file_detection(const wcstring &str) { } } -// Very simple, just mark that we have no more pending items. +/// Very simple, just mark that we have no more pending items. void history_t::resolve_pending() { scoped_lock locker(lock); this->has_pending_item = false; From 2f8d0e9aba3662fcb1032ffe33bc6faf2e04eb8f Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sat, 16 Apr 2016 20:03:14 -0700 Subject: [PATCH 120/363] add way to comment/uncomment a command Fixes #2375 --- .../__fish_toggle_comment_commandline.fish | 17 +++++++++++++++++ share/functions/fish_default_key_bindings.fish | 5 ++++- share/functions/fish_vi_key_bindings.fish | 8 ++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 share/functions/__fish_toggle_comment_commandline.fish diff --git a/share/functions/__fish_toggle_comment_commandline.fish b/share/functions/__fish_toggle_comment_commandline.fish new file mode 100644 index 000000000..8bdd7e822 --- /dev/null +++ b/share/functions/__fish_toggle_comment_commandline.fish @@ -0,0 +1,17 @@ +# This is meant to be bound to key sequences such as \e#. It provides a simple way to quickly +# comment/uncomment the current command. This is something introduced by the Korn shell (ksh) in +# 1993. It allows you to capture a command in the shell history without executing it. Then +# retrieving the command from the shell history and removing the comment chars. +# +# This deliberately does not execute the command when removing the comment characters to give you an +# opportunity to modify the command. + +function __fish_toggle_comment_commandline --description 'Comment/uncomment the current command' + set -l cmdlines (commandline -b) + if test "$cmdlines" = "" + return + end + set -l cmdlines (printf '%s\n' '#'$cmdlines | string replace -r '^##' '') + commandline -r $cmdlines + string match -q '#*' $cmdlines[1]; and commandline -f execute +end diff --git a/share/functions/fish_default_key_bindings.fish b/share/functions/fish_default_key_bindings.fish index 0f0d578cb..c057754ab 100644 --- a/share/functions/fish_default_key_bindings.fish +++ b/share/functions/fish_default_key_bindings.fish @@ -144,5 +144,8 @@ function fish_default_key_bindings -d "Default (Emacs-like) key bindings for fis bind $argv \eOc forward-word bind $argv \eOd backward-word end -end + # Make it easy to turn an unexecuted command into a comment in the shell history. Also, + # remove the commenting chars so the command can be further edited then executed. + bind \e\# __fish_toggle_comment_commandline +end diff --git a/share/functions/fish_vi_key_bindings.fish b/share/functions/fish_vi_key_bindings.fish index b1fc26594..c2a40361e 100644 --- a/share/functions/fish_vi_key_bindings.fish +++ b/share/functions/fish_vi_key_bindings.fish @@ -222,4 +222,12 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' bind -M visual -m default \e end-selection force-repaint set fish_bind_mode $init_mode + + # Make it easy to turn an unexecuted command into a comment in the shell history. Also, remove + # the commenting chars so the command can be further edited then executed. + bind -M default \# __fish_toggle_comment_commandline + bind -M visual \# __fish_toggle_comment_commandline + bind -M default \e\# __fish_toggle_comment_commandline + bind -M insert \e\# __fish_toggle_comment_commandline + bind -M visual \e\# __fish_toggle_comment_commandline end From f28a1c58f3a91c7ed97e1fbf1598a69c234cf9cb Mon Sep 17 00:00:00 2001 From: David Adam Date: Wed, 20 Apr 2016 14:34:12 +0800 Subject: [PATCH 121/363] build_tools/make_pkg: fixups for versioning and Xcode preferences [ci skip] --- build_tools/make_pkg.sh | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/build_tools/make_pkg.sh b/build_tools/make_pkg.sh index 1b041bdcd..7bc7c2eee 100755 --- a/build_tools/make_pkg.sh +++ b/build_tools/make_pkg.sh @@ -1,9 +1,13 @@ #!/bin/sh -VERSION=`sed -E -n 's/^.*PACKAGE_VERSION "([0-9.]+)"/\1/p' osx/config.h` +VERSION=`git describe --always --dirty 2>/dev/null` if test -z "$VERSION" ; then - echo "Could not get version from osx/config.h" - exit 1 + echo "Could not get version from git" + VERSION=`sed -E -n 's/^.*PACKAGE_VERSION "([0-9a-z.\-]+)"/\1/p' osx/config.h` + if test -z "$VERSION"; then + echo "Could not get version from osx/config.h" + exit 1 + fi fi echo "Version is $VERSION" @@ -20,11 +24,11 @@ mkdir -p /tmp/fish_pkg/root /tmp/fish_pkg/intermediates /tmp/fish_pkg/dst xcodebuild install -scheme install_tree -configuration Release DSTROOT=/tmp/fish_pkg/root/ pkgbuild --scripts build_tools/osx_package_scripts --root /tmp/fish_pkg/root/ --identifier 'com.ridiculousfish.fish-shell-pkg' --version "$VERSION" /tmp/fish_pkg/intermediates/fish.pkg -productbuild --package-path /tmp/fish_pkg/intermediates --distribution build_tools/osx_distribution.xml --resources build_tools/osx_package_resources/ ~/fish_built/fish.pkg +productbuild --package-path /tmp/fish_pkg/intermediates --distribution build_tools/osx_distribution.xml --resources build_tools/osx_package_resources/ ~/fish_built/fish-$VERSION.pkg # Make the app -xcodebuild -scheme fish.app -configuration Release DSTROOT=/tmp/fish_app/ +xcodebuild -scheme fish.app -configuration Release DSTROOT=/tmp/fish_app/ SYMROOT=DerivedData/fish/Build/Products rm -f ~/fish_built/fish.app.zip cd DerivedData/fish/Build/Products/Release/ -zip -r ~/fish_built/fish.app.zip fish.app +zip -r ~/fish_built/fish-$VERSION.app.zip fish.app From 413ac86da2d51d0622b26a2428e7f4ed5d4f1102 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Thu, 21 Apr 2016 01:16:49 -0700 Subject: [PATCH 122/363] Don't ship libpcre2.a in the OS X installer package This fix prevents the copy-to-install-path phase for this target in Xcode by setting the SKIP_INSTALL flag. --- fish.xcodeproj/project.pbxproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fish.xcodeproj/project.pbxproj b/fish.xcodeproj/project.pbxproj index b106ec40c..9c7b3f8e4 100644 --- a/fish.xcodeproj/project.pbxproj +++ b/fish.xcodeproj/project.pbxproj @@ -1645,6 +1645,7 @@ GCC_WARN_64_TO_32_BIT_CONVERSION = NO; GCC_WARN_UNUSED_VARIABLE = NO; PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/osx/pcre2 $(SRCROOT)/osx/shared_headers/"; USE_HEADERMAP = NO; WARNING_CFLAGS = ""; @@ -1669,6 +1670,7 @@ GCC_WARN_64_TO_32_BIT_CONVERSION = NO; GCC_WARN_UNUSED_VARIABLE = NO; PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/osx/pcre2 $(SRCROOT)/osx/shared_headers/"; USE_HEADERMAP = NO; WARNING_CFLAGS = ""; @@ -1693,6 +1695,7 @@ GCC_WARN_64_TO_32_BIT_CONVERSION = NO; GCC_WARN_UNUSED_VARIABLE = NO; PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/osx/pcre2 $(SRCROOT)/osx/shared_headers/"; USE_HEADERMAP = NO; WARNING_CFLAGS = ""; From 0e7ab7a7d08c48f80d1ea66f15b4b28588d95f2f Mon Sep 17 00:00:00 2001 From: David Adam Date: Thu, 21 Apr 2016 22:33:29 +0800 Subject: [PATCH 123/363] configure: work harder to ensure mkostemp usability Closes #2952. --- configure.ac | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 90065e9ba..2e3409bc3 100644 --- a/configure.ac +++ b/configure.ac @@ -522,10 +522,12 @@ AC_STRUCT_DIRENT_D_TYPE AC_CHECK_FUNCS( wcsdup wcsndup wcslen wcscasecmp wcsncasecmp fwprintf ) AC_CHECK_FUNCS( futimes wcwidth wcswidth wcstok fputwc fgetwc ) -AC_CHECK_FUNCS( wcstol wcslcat wcslcpy lrand48_r killpg mkostemp ) +AC_CHECK_FUNCS( wcstol wcslcat wcslcpy lrand48_r killpg ) AC_CHECK_FUNCS( backtrace backtrace_symbols_fd sysconf getifaddrs ) AC_CHECK_FUNCS( futimens clock_gettime ) +AC_CHECK_DECL( [mkostemp], [ AC_CHECK_FUNCS([mkostemp]) ] ) + if test x$local_gettext != xno; then AC_CHECK_FUNCS( gettext dcgettext ) From 375bef44432a0fad7ebbd3634a3e950e2d457ec1 Mon Sep 17 00:00:00 2001 From: David Adam Date: Thu, 21 Apr 2016 22:34:37 +0800 Subject: [PATCH 124/363] configure: enable GNU/POSIX extensions by default Reenable mkostemp on Cygwin. Work on #2952. --- configure.ac | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.ac b/configure.ac index 2e3409bc3..537e9dc59 100644 --- a/configure.ac +++ b/configure.ac @@ -92,6 +92,7 @@ AC_PROG_CXX([g++ c++]) AC_PROG_INSTALL AC_PROG_SED AC_LANG(C++) +AC_USE_SYSTEM_EXTENSIONS echo "CXXFLAGS: $CXXFLAGS" From b8817215dcfa6a3d069ef77e43d116983c855982 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sat, 23 Apr 2016 12:26:57 -0700 Subject: [PATCH 125/363] trivial fixes to make doxygen happy --- doc_src/bind.txt | 4 ++-- doc_src/echo.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc_src/bind.txt b/doc_src/bind.txt index f645e32d8..c76795e4d 100644 --- a/doc_src/bind.txt +++ b/doc_src/bind.txt @@ -131,7 +131,7 @@ The following special input functions are available: \subsection bind-example Examples \fish -bind \cd 'exit' +bind \\cd 'exit' \endfish Causes `fish` to exit when @key{Control,D} is pressed. @@ -142,7 +142,7 @@ Performs a history search when the @key{Page Up} key is pressed. \fish set -g fish_key_bindings fish_vi_key_bindings -bind -M insert \cc kill-whole-line force-repaint +bind -M insert \\cc kill-whole-line force-repaint \endfish Turns on Vi key bindings and rebinds @key{Control,C} to clear the input line. diff --git a/doc_src/echo.txt b/doc_src/echo.txt index 75e9ce100..69d6df4ef 100644 --- a/doc_src/echo.txt +++ b/doc_src/echo.txt @@ -55,6 +55,6 @@ echo 'Hello World' Print hello world to stdout \fish -echo -e 'Top\nBottom' +echo -e 'Top\\nBottom' \endfish Print Top and Bottom on separate lines, using an escape sequence From 5df8fab4637865cc895bc698410efedc765dfd8c Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sat, 23 Apr 2016 21:14:00 -0700 Subject: [PATCH 126/363] replace command with \outp in docs Doxygen has been warning that `` and `` are not valid XML/HTML commands since commit cb6d5d76 on 20016-04-04. That's primarily because there is at present no way to tell Doxygen to recognize new XML/HTML tags. The actual errors look like this: ``` .../string.doxygen:187: warning: Unsupported xml/html tag found ``` I hate build errors since they a) cause needless concern, and b) make it harder to notice when I've introduced a new error. So switch from XML/C## style markup to Doxygen style markup for the "outp" annotation. --- doc_src/FORMATTING.md | 4 +- doc_src/index.hdr.in | 10 ++--- doc_src/prompt_pwd.txt | 8 ++-- doc_src/string.txt | 94 +++++++++++++++++++------------------- doc_src/tutorial.hdr | 100 ++++++++++++++++++++--------------------- doc_src/type.txt | 2 +- lexicon_filter.in | 2 + 7 files changed, 111 insertions(+), 109 deletions(-) diff --git a/doc_src/FORMATTING.md b/doc_src/FORMATTING.md index df6de6f8d..4b4b23381 100644 --- a/doc_src/FORMATTING.md +++ b/doc_src/FORMATTING.md @@ -158,10 +158,10 @@ The following can be used in \\fish blocks to render some fish scenarios. These - ``: \Matched\ items, such as tab completions. - ``: Matched items \searched\ for, like grep results. - ``: \This would be shown as an error.\ -- ``: \This test will not be parsed for fish markup.\ -- ``: \This would be rendered as command/script output.\ +- ``: \This text will not be parsed for fish markup.\ - ``: Render the contents with a preceding backslash. Useful when presenting output. - `{{` and `}}`: Required when wanting curly braces in regular expression example. +- `\\outp`: \\outp\{This would be rendered as command/script output.\} ### Prompts and cursors diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index 7e889d8e6..64ed2eb79 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -551,22 +551,22 @@ Lists adjacent to other lists or strings are expanded as cartesian products: Examples: \fish{cli-dark} >_ echo {good,bad}" apples" -good apples bad apples +\outp{good apples bad apples} >_ set -l a x y z >_ set -l b 1 2 3 >_ echo $a$b -x1 y1 z1 x2 y2 z2 x3 y3 z3 +\outp{x1 y1 z1 x2 y2 z2 x3 y3 z3} >_ echo $a"-"$b -x-1 y-1 z-1 x-2 y-2 z-2 x-3 y-3 z-3 +\outp{x-1 y-1 z-1 x-2 y-2 z-2 x-3 y-3 z-3} >_ echo {x,y,z}$b -x1 y1 z1 x2 y2 z2 x3 y3 z3 +\outp{x1 y1 z1 x2 y2 z2 x3 y3 z3} >_ echo {$b}word -1word 2word 3word +\outp{1word 2word 3word} \endfish Be careful when you try to use braces to separate variable names from text. The dangers noted in the last example above can be avoided by wrapping the variable in double quotes instead of braces (`echo "$b"word`). diff --git a/doc_src/prompt_pwd.txt b/doc_src/prompt_pwd.txt index 0eafbf994..039efabb8 100644 --- a/doc_src/prompt_pwd.txt +++ b/doc_src/prompt_pwd.txt @@ -16,16 +16,16 @@ To change the number of characters per path component, set $fish_prompt_pwd_dir_ \fish{cli-dark} >_ cd ~/ >_ echo $PWD -/home/alfa +\outp{/home/alfa} >_ prompt_pwd -~ +\outp{~} >_ cd /tmp/banana/sausage/with/mustard >_ prompt_pwd -/t/b/s/w/mustard +\outp{/t/b/s/w/mustard} >_ set -g fish_prompt_pwd_dir_length 3 >_ prompt_pwd -/tmp/ban/sau/wit/mustard +\outp{/tmp/ban/sau/wit/mustard} \endfish diff --git a/doc_src/string.txt b/doc_src/string.txt index 58de93e40..5a7b6f37e 100644 --- a/doc_src/string.txt +++ b/doc_src/string.txt @@ -53,7 +53,7 @@ The following subcommands are available: \fish{cli-dark} >_ string length 'hello, world' -12 +\outp{12} >_ set str foo >_ string length -q $str; echo $status @@ -63,42 +63,42 @@ The following subcommands are available: \fish{cli-dark} >_ string sub --length 2 abcde -ab +\outp{ab} >_ string sub -s 2 -l 2 abcde -bc +\outp{bc} >_ string sub --start=-2 abcde -de +\outp{de} \endfish \fish{cli-dark} >_ string split . example.com -example -com +\outp{example} +\outp{com} >_ string split -r -m1 / /usr/local/bin/fish -/usr/local/bin -fish +\outp{/usr/local/bin} +\outp{fish} >_ string split '' abc -a -b -c +\outp{a} +\outp{b} +\outp{c} \endfish \fish{cli-dark} >_ seq 3 | string join ... -1...2...3 +\outp{1...2...3} \endfish \fish{cli-dark} >_ string trim ' abc ' -abc +\outp{abc} >_ string trim --right --chars=yz xyzzy zany -x -zan +\outp{x} +\outp{zan} \endfish \fish{cli-dark} @@ -110,78 +110,78 @@ The following subcommands are available: \fish{cli-dark} >_ string match '?' a -a +\outp{a} >_ string match 'a*b' axxb -axxb +\outp{axxb} >_ string match -i 'a??B' Axxb -Axxb +\outp{Axxb} >_ echo 'ok?' | string match '*\\?' ->_ ok? +>_ \outp{ok?} >_ string match -r -v "c.*[12]" {cat,dog}(seq 1 4) -dog1 -dog2 -cat3 -dog3 -cat4 -dog4 +\outp{dog1} +\outp{dog2} +\outp{cat3} +\outp{dog3} +\outp{cat4} +\outp{dog4} \endfish \subsection string-example-match-regex Match Regex Examples \fish{cli-dark} >_ string match -r 'cat|dog|fish' 'nice dog' -dog +\outp{dog} >_ string match -r '(\\d\\d?):(\\d\\d):(\\d\\d)' 2:34:56 -2:34:56 -2 -34 -56 +\outp{2:34:56} +\outp{2} +\outp{34} +\outp{56} >_ string match -r '^(\\w{{2,4}})\\g1$' papa mud murmur -papa -pa -murmur -mur +\outp{papa} +\outp{pa} +\outp{murmur} +\outp{mur} >_ string match -r -a -n at ratatat -2 2 -4 2 -6 2 +\outp{2 2} +\outp{4 2} +\outp{6 2} >_ string match -r -i '0x[0-9a-f]{{1,8}}' 'int magic = 0xBadC0de;' -0xBadC0de +\outp{0xBadC0de} \endfish \subsection string-example-replace-literal Replace Literal Examples \fish{cli-dark} >_ string replace is was 'blue is my favorite' -blue was my favorite +\outp{blue was my favorite} >_ string replace 3rd last 1st 2nd 3rd -1st -2nd -last +\outp{1st} +\outp{2nd} +\outp{last} >_ string replace -a ' ' _ 'spaces to underscores' -spaces_to_underscores +\outp{spaces_to_underscores} \endfish \subsection string-example-replace-Regex Replace Regex Examples \fish{cli-dark} >_ string replace -r -a '[^\\d.]+' ' ' '0 one two 3.14 four 5x' -0 3.14 5 +\outp{0 3.14 5} >_ string replace -r '(\\w+)\\s+(\\w+)' '$2 $1 $$' 'left right' -right left $ +\outp{right left $} >_ string replace -r '\\s*newline\\s*' '\\n' 'put a newline here' -put a -here +\outp{put a} +\outp{here} \endfish diff --git a/doc_src/tutorial.hdr b/doc_src/tutorial.hdr index cb8427d4b..9c94cca35 100644 --- a/doc_src/tutorial.hdr +++ b/doc_src/tutorial.hdr @@ -54,8 +54,8 @@ If you have a strong understanding of other shells, and want to know what `fish` When you start `fish`, you should see this: \fish{cli-dark} -Welcome to fish, the friendly interactive shell -Type help for instructions on how to use fish +\outp{Welcome to fish, the friendly interactive shell} +\outp{Type help for instructions on how to use fish} you@hostname ~>____ \endfish @@ -68,7 +68,7 @@ When you start `fish`, you should see this: \fish{cli-dark} >_ echo hello world -hello world +\outp{hello world} \endfish You can include a literal space in an argument with a backslash, or by using single or double quotes: @@ -77,7 +77,7 @@ You can include a literal space in an argument with a backslash, or by using sin >_ mkdir My\ Files >_ cp ~/Some\ File 'My Files' >_ ls "My Files" -Some File +\outp{Some File} \endfish Commands can be chained with semicolons. @@ -89,8 +89,8 @@ Commands can be chained with semicolons. \fish{cli-dark} >_ man set -set - handle shell variables - Synopsis... +\outp{set - handle shell variables} +\outp{ Synopsis...} \endfish @@ -125,25 +125,25 @@ These colors, and many more, can be changed by running `fish_config`, or by modi \fish{cli-dark} >_ ls *.jpg -lena.jpg -meena.jpg -santa maria.jpg +\outp{lena.jpg} +\outp{meena.jpg} +\outp{santa maria.jpg} \endfish You can include multiple wildcards: \fish{cli-dark} >_ ls l*.p* -lena.png -lesson.pdf +\outp{lena.png} +\outp{lesson.pdf} \endfish Especially powerful is the recursive wildcard ** which searches directories recursively: \fish{cli-dark} >_ ls /var/**.log -/var/log/system.log -/var/run/sntp.log +\outp{/var/log/system.log} +\outp{/var/run/sntp.log} \endfish If that directory traversal is taking a long time, you can @key{Control,C} out of it. @@ -155,7 +155,7 @@ You can pipe between commands with the usual vertical bar: \fish{cli-dark} >_ echo hello world | wc - 1 2 12 +\outp{ 1 2 12} \endfish stdin and stdout can be redirected via the familiar < and >. Unlike other shells, stderr is redirected with a caret ^ @@ -201,7 +201,7 @@ If there's more than one possibility, it will list them: \fish{cli-dark} >_ ~/stuff/s @key{Tab} -~/stuff/script.sh (Executable, 4.8kB) ~/stuff/sources/ (Directory) +\outp{~/stuff/script.sh (Executable, 4.8kB) ~/stuff/sources/ (Directory)} \endfish Hit tab again to cycle through the possibilities. @@ -211,7 +211,7 @@ Hit tab again to cycle through the possibilities. \fish{cli-dark} >_ git merge pr @key{Tab} → git merge prompt_designer >_ git checkout b @key{Tab} -builtin_list_io_merge (Branch) builtin_set_color (Branch) busted_events (Tag) +\outp{builtin_list_io_merge (Branch) builtin_set_color (Branch) busted_events (Tag)} \endfish Try hitting tab and see what `fish` can do! @@ -222,16 +222,16 @@ Like other shells, a dollar sign performs variable substitution: \fish{cli-dark} >_ echo My home directory is $HOME -My home directory is /home/tutorial +\outp{My home directory is /home/tutorial} \endfish Variable substitution also occurs in double quotes, but not single quotes: \fish{cli-dark} >_ echo "My current directory is $PWD" -My current directory is /home/tutorial +\outp{My current directory is /home/tutorial} >_ echo 'My current directory is $PWD' -My current directory is $PWD +\outp{My current directory is $PWD} \endfish Unlike other shells, `fish` has no dedicated syntax for setting variables. Instead it has an ordinary command: `set`, which takes a variable name, and then its value. @@ -239,7 +239,7 @@ Unlike other shells, `fish` has no dedicated syntax for setting variables. Inste \fish{cli-dark} >_ set name 'Mister Noodle' >_ echo $name -Mister Noodle +\outp{Mister Noodle} \endfish (Notice the quotes: without them, `Mister` and `Noodle` would have been separate arguments, and `$name` would have been made into a list of two elements.) @@ -249,7 +249,7 @@ Unlike other shells, variables are not further split after substitution: \fish{cli-dark} >_ mkdir $name >_ ls -Mister Noodle +\outp{Mister Noodle} \endfish In bash, this would have created two directories "Mister" and "Noodle". In `fish`, it created only one: the variable had the value "Mister Noodle", so that is the argument that was passed to `mkdir`, spaces and all. Other shells use the term "arrays", rather than lists. @@ -262,7 +262,7 @@ Unlike other shells, `fish` stores the exit status of the last command in `$stat \fish{cli-dark} >_ false >_ echo $status -1 +\outp{1} \endfish Zero is considered success, and non-zero is failure. @@ -275,7 +275,7 @@ Unlike other shells, `fish` does not have an export command. Instead, a variable \fish{cli-dark} >_ set -x MyVariable SomeValue >_ env | grep MyVariable -MyVariable=SomeValue +\outp{MyVariable=SomeValue} \endfish You can erase a variable with `-e` or `--erase` @@ -283,7 +283,7 @@ You can erase a variable with `-e` or `--erase` \fish{cli-dark} >_ set -e MyVariable >_ env | grep MyVariable -(no output) +\outp{(no output)} \endfish @@ -297,7 +297,7 @@ Other variables, like `$PATH`, really do have multiple values. During variable e \fish{cli-dark} >_ echo $PATH -/usr/bin /bin /usr/sbin /sbin /usr/local/bin +\outp{/usr/bin /bin /usr/sbin /sbin /usr/local/bin} \endfish Lists cannot contain other lists: there is no recursion. A variable is a list of strings, full stop. @@ -306,7 +306,7 @@ Get the length of a list with `count`: \fish{cli-dark} >_ count $PATH -5 +\outp{5} \endfish You can append (or prepend) to a list by setting the list to itself, with some additional arguments. Here we append /usr/local/bin to $PATH: @@ -320,20 +320,20 @@ You can access individual elements with square brackets. Indexing starts at 1 fr \fish{cli-dark} >_ echo $PATH -/usr/bin /bin /usr/sbin /sbin /usr/local/bin +\outp{/usr/bin /bin /usr/sbin /sbin /usr/local/bin} >_ echo $PATH[1] -/usr/bin +\outp{/usr/bin} >_ echo $PATH[-1] -/usr/local/bin +\outp{/usr/local/bin} \endfish You can also access ranges of elements, known as "slices:" \fish{cli-dark} >_ echo $PATH[1..2] -/usr/bin /bin +\outp{/usr/bin /bin} >_ echo $PATH[-1..2] -/usr/local/bin /sbin /usr/sbin /bin +\outp{/usr/local/bin /sbin /usr/sbin /bin} \endfish You can iterate over a list (or a slice) with a for loop: @@ -342,11 +342,11 @@ You can iterate over a list (or a slice) with a for loop: >_ for val in $PATH echo "entry: $val" end -entry: /usr/bin/ -entry: /bin -entry: /usr/sbin -entry: /sbin -entry: /usr/local/bin +\outp{entry: /usr/bin/} +\outp{entry: /bin} +\outp{entry: /usr/sbin} +\outp{entry: /sbin} +\outp{entry: /usr/local/bin} \endfish Lists adjacent to other lists or strings are expanded as cartesian products unless quoted (see Variable expansion): @@ -355,11 +355,11 @@ Lists adjacent to other lists or strings are expanded as 1 banana 2 banana 3 banana +\outp{1 banana 2 banana 3 banana} >_ echo "$a banana" -1 2 3 banana +\outp{1 2 3 banana} \endfish This is similar to Brace expansion. @@ -370,7 +370,7 @@ Command substitutions use the output of one command as an argument to another. U \fish{cli-dark} >_ echo In (pwd), running (uname) -In /home/tutorial, running FreeBSD +\outp{In /home/tutorial, running FreeBSD} \endfish A common idiom is to capture the output of a command in a variable: @@ -378,7 +378,7 @@ A common idiom is to capture the output of a command in a variable: \fish{cli-dark} >_ set os (uname) >_ echo $os -Linux +\outp{Linux} \endfish Command substitutions are not expanded within quotes. Instead, you can temporarily close the quotes, add the command substitution, and reopen them, all in the same argument: @@ -386,7 +386,7 @@ Command substitutions are not expanded within quotes. Instead, you can temporari \fish{cli-dark} >_ touch "testing_"(date +%s)".txt" >_ ls *.txt -testing_1360099791.txt +\outp{testing_1360099791.txt} \endfish @@ -396,7 +396,7 @@ Unlike other shells, `fish` does not have special syntax like && or || t \fish{cli-dark} >_ cp file1.txt file1_bak.txt; and echo "Backup successful"; or echo "Backup failed" -Backup failed +\outp{Backup failed} \endfish @@ -441,9 +441,9 @@ A `fish` function is a list of commands, which may optionally take arguments. Un echo Hello $argv end >_ say_hello -Hello +\outp{Hello} >_ say_hello everybody! -Hello everybody! +\outp{Hello everybody!} \endfish Unlike other shells, `fish` does not have aliases or special prompt syntax. Functions take their place. @@ -452,7 +452,7 @@ You can list the names of all functions with the `functions` keyword (note the p \fish{cli-dark} >_ functions -alias, cd, delete-or-exit, dirh, dirs, down-or-search, eval, export, fish_command_not_found_setup, fish_config, fish_default_key_bindings, fish_prompt, fish_right_prompt, fish_sigtrap_handler, fish_update_completions, funced, funcsave, grep, help, history, isatty, ls, man, math, nextd, nextd-or-forward-word, open, popd, prevd, prevd-or-backward-word, prompt_pwd, psub, pushd, seq, setenv, trap, type, umask, up-or-search, vared +\outp{alias, cd, delete-or-exit, dirh, dirs, down-or-search, eval, export, fish_command_not_found_setup, fish_config, fish_default_key_bindings, fish_prompt, fish_right_prompt, fish_sigtrap_handler, fish_update_completions, funced, funcsave, grep, help, history, isatty, ls, man, math, nextd, nextd-or-forward-word, open, popd, prevd, prevd-or-backward-word, prompt_pwd, psub, pushd, seq, setenv, trap, type, umask, up-or-search, vared} \endfish You can see the source for any function by passing its name to `functions`: @@ -473,10 +473,10 @@ While loops: >_ while true echo "Loop forever" end -Loop forever -Loop forever -Loop forever -... +\outp{Loop forever} +\outp{Loop forever} +\outp{Loop forever} +\outp{...} \endfish For loops can be used to iterate over a list. For example, a list of files: diff --git a/doc_src/type.txt b/doc_src/type.txt index 2da88c94d..7b5f7a9c2 100644 --- a/doc_src/type.txt +++ b/doc_src/type.txt @@ -28,5 +28,5 @@ The following options are available: \fish{cli-dark} >_ type fg -fg is a builtin +\outp{fg is a builtin} \endfish diff --git a/lexicon_filter.in b/lexicon_filter.in index e9555e8f2..13e8b6d79 100644 --- a/lexicon_filter.in +++ b/lexicon_filter.in @@ -71,6 +71,8 @@ /<[^>]*>/ { b html } + # Preprocess specially recognized commands. + s/\\outp/@outp/g # Process the rest b process } From dcef1a55932db5ad4394eb6fbcb76d63d8ab520d Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sun, 24 Apr 2016 15:02:52 -0700 Subject: [PATCH 127/363] more doxygen lexicon changes to eliminate errors I noticed that Doxygen was also complaining about the "" and "" tags. So convert those to the backslash form like we did for "" in the previous commit. --- doc_src/FORMATTING.md | 13 +++++++------ doc_src/index.hdr.in | 2 +- doc_src/string.txt | 4 ++-- doc_src/tutorial.hdr | 24 ++++++++++++------------ lexicon_filter.in | 43 ++++++++++--------------------------------- 5 files changed, 32 insertions(+), 54 deletions(-) diff --git a/doc_src/FORMATTING.md b/doc_src/FORMATTING.md index 4b4b23381..5ab4e6acf 100644 --- a/doc_src/FORMATTING.md +++ b/doc_src/FORMATTING.md @@ -154,14 +154,15 @@ The following can be used in \\fish blocks to render some fish scenarios. These ### Custom formatting tags -- ``: auto\suggestion\. -- ``: \Matched\ items, such as tab completions. -- ``: Matched items \searched\ for, like grep results. -- ``: \This would be shown as an error.\ -- ``: \This text will not be parsed for fish markup.\ -- ``: Render the contents with a preceding backslash. Useful when presenting output. - `{{` and `}}`: Required when wanting curly braces in regular expression example. +- `\\asis`: \\asis\{This text will not be parsed for fish markup.\} +- `\\bksl`: \\bksl\{Render the contents with a preceding backslash. Useful when presenting output.} +- `\\eror`: \\eror\{This would be shown as an error.\} +- `\\mtch`: \\mtch\{Matched\} items, such as tab completions. - `\\outp`: \\outp\{This would be rendered as command/script output.\} +- `\\sgst`: auto\\sgst\{suggestion\}. +- `\\smtc`: Matched items \\smtc\{searched\} for, like grep results. +- `\\undr`: \\undr\{These words are underlined\}. ### Prompts and cursors diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index 64ed2eb79..c06d39d89 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -70,7 +70,7 @@ rm "cumbersome filename.txt" Will remove the file 'cumbersome filename.txt', while \fish -rm cumbersome filename.txt +rm \asis{cumbersome filename.txt} \endfish would remove the two files 'cumbersome' and 'filename.txt'. diff --git a/doc_src/string.txt b/doc_src/string.txt index 5a7b6f37e..31677858e 100644 --- a/doc_src/string.txt +++ b/doc_src/string.txt @@ -103,7 +103,7 @@ The following subcommands are available: \fish{cli-dark} >_ echo \\x07 | string escape -cg +\bksl{cg} \endfish \subsection string-example-match-glob Match Glob Examples @@ -136,7 +136,7 @@ The following subcommands are available: >_ string match -r 'cat|dog|fish' 'nice dog' \outp{dog} ->_ string match -r '(\\d\\d?):(\\d\\d):(\\d\\d)' 2:34:56 +>_ string match -r '(\\d\\d?):(\\d\\d):(\\d\\d)' \asis{2:34:56} \outp{2:34:56} \outp{2} \outp{34} diff --git a/doc_src/tutorial.hdr b/doc_src/tutorial.hdr index 9c94cca35..8442b9054 100644 --- a/doc_src/tutorial.hdr +++ b/doc_src/tutorial.hdr @@ -56,7 +56,7 @@ When you start `fish`, you should see this: \fish{cli-dark} \outp{Welcome to fish, the friendly interactive shell} \outp{Type help for instructions on how to use fish} -you@hostname ~>____ +\asis{you@hostname} ~>____ \endfish `fish` comes with a default prompt that shows your username, hostname, and working directory. You'll see how to change your prompt further down. From now on, we'll pretend your prompt is just a '`>`' to save space. @@ -99,7 +99,7 @@ Commands can be chained with semicolons. You'll quickly notice that `fish` performs syntax highlighting as you type. Invalid commands are colored red by default: \fish{cli-dark} ->_ /bin/mkd +>_ \eror{/bin/mkd} \endfish A command may be invalid because it does not exist, or refers to a file that you cannot execute. When the command becomes valid, it is shown in a different color: @@ -111,7 +111,7 @@ A command may be invalid because it does not exist, or refers to a file that you `fish` will underline valid file paths as you type them: \fish{cli-dark} ->_ cat ~/somefi___ +>_ cat \undr{~/somefi}___ \endfish This tells you that there exists a file that starts with '`somefi`', which is useful feedback as you type. @@ -170,19 +170,19 @@ stdin and stdout can be redirected via the familiar < and >. Unlike other `fish` suggests commands as you type, and shows the suggestion to the right of the cursor, in gray. For example: \fish{cli-dark} ->_ /bin/h___ostname +>_ \eror{/bin/h}\sgst{___ostname} \endfish It knows about paths and options: \fish{cli-dark} ->_ grep --i___gnore-case +>_ grep --i\sgst{___gnore-case} \endfish And history too. Type a command once, and you can re-summon it by just typing a few letters: \fish{cli-dark} ->_ r___sync -avze ssh . myname@somelonghost.com:/some/long/path/doo/dee/doo/dee/doo +>_ \eror{r<}\sgst{___sync -avze ssh . myname@somelonghost.com:/some/long/path/doo/dee/doo/dee/doo} \endfish To accept the autosuggestion, hit @cursor_key{→,right arrow} or @key{Control,F}. To accept a single word of the autosuggestion, @key{Alt,→} (right arrow). If the autosuggestion is not what you want, just ignore it. @@ -194,14 +194,14 @@ To accept the autosuggestion, hit @cursor_key{→,right arrow} or @key{Contro Press @key{Tab}, and `fish` will attempt to complete the command, argument, or path: \fish{cli-dark} ->_ /pri @key{Tab} → /private/ +>_ \eror{/pri} @key{Tab} → /private/ \endfish If there's more than one possibility, it will list them: \fish{cli-dark} ->_ ~/stuff/s @key{Tab} -\outp{~/stuff/script.sh (Executable, 4.8kB) ~/stuff/sources/ (Directory)} +>_ \eror{~/stuff/s} @key{Tab} +\outp{\mtch{~/stuff/s}cript.sh (Executable, 4.8kB) \mtch{~/stuff/s}ources/ (Directory)} \endfish Hit tab again to cycle through the possibilities. @@ -211,7 +211,7 @@ Hit tab again to cycle through the possibilities. \fish{cli-dark} >_ git merge pr @key{Tab} → git merge prompt_designer >_ git checkout b @key{Tab} -\outp{builtin_list_io_merge (Branch) builtin_set_color (Branch) busted_events (Tag)} +\outp{\mtch{b}uiltin_list_io_merge (Branch) \mtch{b}uiltin_set_color (Branch) \mtch{b}usted_events (Tag)} \endfish Try hitting tab and see what `fish` can do! @@ -275,7 +275,7 @@ Unlike other shells, `fish` does not have an export command. Instead, a variable \fish{cli-dark} >_ set -x MyVariable SomeValue >_ env | grep MyVariable -\outp{MyVariable=SomeValue} +\outp{\smtc{MyVariablem}=SomeValue} \endfish You can erase a variable with `-e` or `--erase` @@ -506,7 +506,7 @@ You can define your own prompt: >_ function fish_prompt echo "New Prompt % " end -New Prompt % ___ +\asis{New Prompt % }___ \endfish Multiple lines are OK. Colors can be set via `set_color`, passing it named ANSI colors, or hex RGB values: diff --git a/lexicon_filter.in b/lexicon_filter.in index 13e8b6d79..b618a1b78 100644 --- a/lexicon_filter.in +++ b/lexicon_filter.in @@ -72,7 +72,16 @@ b html } # Preprocess specially recognized commands. + s/\\asis/@asis/g + s/\\bksl/@bksl/g + s/\\bold/@bold/g + s/\\emph/@emph/g + s/\\eror/@eror/g + s/\\mtch/@mtch/g s/\\outp/@outp/g + s/\\sgst/@sgst/g + s/\\smtc/@smtc/g + s/\\undr/@undr/g # Process the rest b process } @@ -96,7 +105,7 @@ s||@bold{| s|]*>|@bold{| s||}| #. -# Strong (synonimous with emphasis) +# Strong (synonymous with emphasis) s||@bold{| s|]*>|@bold{| s||}| @@ -115,39 +124,7 @@ s||}| s||@undr{| s|]*>|@undr{| s||}| -# Backslash (when escaping output) -s||@bksl{| -s||}| -t html #. -# Some handy non-standard extensions -# autoSuGgeSTion -s||@sgst{| -s|]*>|@sgst{| -s||}| -#. -# MaTCH -s||@mtch{| -s|]*>|@mtch{| -s||}| -#. -# SearchMaTCh -s||@smtc{| -s|]*>|@smtc{| -s||}| -#. -# ERrOR -s||@eror{| -s|]*>|@eror{| -s||}| -#. -# AsIs - protect from auto-formatting -s||@asis{| -s||}| -#. -# OUTPut - protect from auto-formatting -s||@outp{| -s||}| t html #. # Clean other unhandled html From 8ab980b793e80199f61257b1bc46826a93af8413 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 12 Apr 2016 20:32:29 +0200 Subject: [PATCH 128/363] Remove $__fish_vi_mode This makes fish_mode_prompt rely on $fish_key_bindings instead. fish_bind_mode is also set in default mode (only always "default"), so it can't be used as the indicator. --- share/functions/fish_mode_prompt.fish | 2 +- share/functions/fish_vi_mode.fish | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/share/functions/fish_mode_prompt.fish b/share/functions/fish_mode_prompt.fish index e9b49e5b3..eed75d42d 100644 --- a/share/functions/fish_mode_prompt.fish +++ b/share/functions/fish_mode_prompt.fish @@ -1,7 +1,7 @@ # The fish_mode_prompt function is prepended to the prompt function fish_mode_prompt --description "Displays the current mode" # Do nothing if not in vi mode - if set -q __fish_vi_mode + if test "$fish_key_bindings" = "fish_vi_key_bindings" switch $fish_bind_mode case default set_color --bold --background red white diff --git a/share/functions/fish_vi_mode.fish b/share/functions/fish_vi_mode.fish index b4fbe63a7..8c89a53e2 100644 --- a/share/functions/fish_vi_mode.fish +++ b/share/functions/fish_vi_mode.fish @@ -1,8 +1,4 @@ function fish_vi_mode - # Set the __fish_vi_mode variable - # This triggers fish_mode_prompt to output the mode indicator - set -g __fish_vi_mode 1 - # Turn on vi keybindings set -g fish_key_bindings fish_vi_key_bindings end From f5241da836213b8ee05d4946e4884be2a6714658 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 12 Apr 2016 20:36:46 +0200 Subject: [PATCH 129/363] Let the binding functions set the binding variable This ensures they can just be called and "the right thing" will happen - fish_user_key_bindings will be executed, the variable will reflect the bindings. --- share/functions/fish_default_key_bindings.fish | 4 ++++ share/functions/fish_vi_key_bindings.fish | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/share/functions/fish_default_key_bindings.fish b/share/functions/fish_default_key_bindings.fish index c057754ab..e679fac08 100644 --- a/share/functions/fish_default_key_bindings.fish +++ b/share/functions/fish_default_key_bindings.fish @@ -1,6 +1,10 @@ function fish_default_key_bindings -d "Default (Emacs-like) key bindings for fish" -a mode if not set -q mode[1] + if test "$fish_key_bindings" != "fish_default_key_bindings" + set fish_key_bindings fish_default_key_bindings # This triggers the handler, which calls us again and ensures the user_key_bindings are executed + return + end # Clear earlier bindings, if any bind --erase --all end diff --git a/share/functions/fish_vi_key_bindings.fish b/share/functions/fish_vi_key_bindings.fish index c2a40361e..1956a11bd 100644 --- a/share/functions/fish_vi_key_bindings.fish +++ b/share/functions/fish_vi_key_bindings.fish @@ -1,4 +1,8 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' + if test "$fish_key_bindings" != "fish_vi_key_bindings" + set fish_key_bindings fish_vi_key_bindings # This triggers the handler, which calls us again and ensures the user_key_bindings are executed + return + end # The default escape timeout is 300ms. But for users of Vi bindings that can # be slightly annoying when trying to switch to Vi "normal" mode. Too, # vi-mode users are unlikely to use escape-as-meta. So set a much shorter From ba5a22e2ce3b84eda1f3e03f8089643971d0f742 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 12 Apr 2016 20:41:35 +0200 Subject: [PATCH 130/363] Deprecate fish_vi_mode This was never mentioned in the documentation as the way to switch to vi-mode, and now does nothing of value anymore. --- doc_src/fish_vi_mode.txt | 2 ++ share/functions/fish_vi_mode.fish | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/doc_src/fish_vi_mode.txt b/doc_src/fish_vi_mode.txt index 3676dedd2..d39697c8f 100644 --- a/doc_src/fish_vi_mode.txt +++ b/doc_src/fish_vi_mode.txt @@ -7,4 +7,6 @@ fish_vi_mode \subsection fish_vi_mode-description Description +This function is deprecated. Please call `fish_vi_key_bindings directly` + `fish_vi_mode` enters a vi-like command editing mode. To always start in vi mode, add `fish_vi_mode` to your `config.fish` file. diff --git a/share/functions/fish_vi_mode.fish b/share/functions/fish_vi_mode.fish index 8c89a53e2..29ddeb6c9 100644 --- a/share/functions/fish_vi_mode.fish +++ b/share/functions/fish_vi_mode.fish @@ -1,4 +1,5 @@ function fish_vi_mode - # Turn on vi keybindings - set -g fish_key_bindings fish_vi_key_bindings + echo "This function is deprecated. Please call fish_vi_key_bindings directly" >&2 + # Turn on vi keybindings + set -g fish_key_bindings fish_vi_key_bindings end From e6ad48ea1b129471d279d45011b3e6de7fd3b62d Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 14 Apr 2016 18:28:32 +0200 Subject: [PATCH 131/363] Set fish_key_bindings globally in binding functions This should fix the tests. --- share/functions/fish_default_key_bindings.fish | 2 +- share/functions/fish_vi_key_bindings.fish | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/share/functions/fish_default_key_bindings.fish b/share/functions/fish_default_key_bindings.fish index e679fac08..64f5115f7 100644 --- a/share/functions/fish_default_key_bindings.fish +++ b/share/functions/fish_default_key_bindings.fish @@ -2,7 +2,7 @@ function fish_default_key_bindings -d "Default (Emacs-like) key bindings for fish" -a mode if not set -q mode[1] if test "$fish_key_bindings" != "fish_default_key_bindings" - set fish_key_bindings fish_default_key_bindings # This triggers the handler, which calls us again and ensures the user_key_bindings are executed + set -g fish_key_bindings fish_default_key_bindings # This triggers the handler, which calls us again and ensures the user_key_bindings are executed return end # Clear earlier bindings, if any diff --git a/share/functions/fish_vi_key_bindings.fish b/share/functions/fish_vi_key_bindings.fish index 1956a11bd..d46b7ccdb 100644 --- a/share/functions/fish_vi_key_bindings.fish +++ b/share/functions/fish_vi_key_bindings.fish @@ -1,6 +1,6 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' if test "$fish_key_bindings" != "fish_vi_key_bindings" - set fish_key_bindings fish_vi_key_bindings # This triggers the handler, which calls us again and ensures the user_key_bindings are executed + set -g fish_key_bindings fish_vi_key_bindings # This triggers the handler, which calls us again and ensures the user_key_bindings are executed return end # The default escape timeout is 300ms. But for users of Vi bindings that can From 7ebafa53f6b97037fa0841c2049269d10a2378d6 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 15 Apr 2016 01:29:27 +0200 Subject: [PATCH 132/363] Tests: Add fish_mode_prompt to except_prompt Without this, the interactive tests fail when they receive a mode_prompt in vi-mode. --- tests/interactive.expect.rc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/interactive.expect.rc b/tests/interactive.expect.rc index 1129c176b..b0cc9e89e 100644 --- a/tests/interactive.expect.rc +++ b/tests/interactive.expect.rc @@ -51,7 +51,7 @@ set prompt_counter 1 proc expect_prompt {args} { global prompt_counter upvar expect_out expect_out - set prompt_pat [list -re "(?:\\r\\n?|^)prompt $prompt_counter>(?:$|\\r)"] + set prompt_pat [list -re "(?:\\r\\n?|^)(?:\\\[.\\\] )?prompt $prompt_counter>(?:$|\\r)"] if {[llength $args] == 1 && [string match "\n*" $args]} { set args [join $args] } From e67505bead54b1b1ae50b8657143a705adbd184d Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 15 Apr 2016 10:55:39 +0200 Subject: [PATCH 133/363] Remove named arg in fish_default_key_bindings This wasn't actually used anywhere. --- share/functions/fish_default_key_bindings.fish | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/share/functions/fish_default_key_bindings.fish b/share/functions/fish_default_key_bindings.fish index 64f5115f7..305299643 100644 --- a/share/functions/fish_default_key_bindings.fish +++ b/share/functions/fish_default_key_bindings.fish @@ -1,6 +1,6 @@ -function fish_default_key_bindings -d "Default (Emacs-like) key bindings for fish" -a mode - if not set -q mode[1] +function fish_default_key_bindings -d "Default (Emacs-like) key bindings for fish" + if not set -q argv[1] if test "$fish_key_bindings" != "fish_default_key_bindings" set -g fish_key_bindings fish_default_key_bindings # This triggers the handler, which calls us again and ensures the user_key_bindings are executed return From daa217f533490e0b9bc4113a143e8f38de922b7a Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 15 Apr 2016 13:55:19 +0200 Subject: [PATCH 134/363] Allow setting key bindings universally As always, we default to setting globally. --- share/functions/fish_default_key_bindings.fish | 4 +++- share/functions/fish_vi_key_bindings.fish | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/share/functions/fish_default_key_bindings.fish b/share/functions/fish_default_key_bindings.fish index 305299643..3e9fb1960 100644 --- a/share/functions/fish_default_key_bindings.fish +++ b/share/functions/fish_default_key_bindings.fish @@ -2,7 +2,9 @@ function fish_default_key_bindings -d "Default (Emacs-like) key bindings for fish" if not set -q argv[1] if test "$fish_key_bindings" != "fish_default_key_bindings" - set -g fish_key_bindings fish_default_key_bindings # This triggers the handler, which calls us again and ensures the user_key_bindings are executed + # Allow the user to set the variable universally + set -q fish_key_bindings; or set -g fish_key_bindings + set fish_key_bindings fish_default_key_bindings # This triggers the handler, which calls us again and ensures the user_key_bindings are executed return end # Clear earlier bindings, if any diff --git a/share/functions/fish_vi_key_bindings.fish b/share/functions/fish_vi_key_bindings.fish index d46b7ccdb..bdb2bd8ad 100644 --- a/share/functions/fish_vi_key_bindings.fish +++ b/share/functions/fish_vi_key_bindings.fish @@ -1,6 +1,8 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' if test "$fish_key_bindings" != "fish_vi_key_bindings" - set -g fish_key_bindings fish_vi_key_bindings # This triggers the handler, which calls us again and ensures the user_key_bindings are executed + # Allow the user to set the variable universally + set -q fish_key_bindings; or set -g fish_key_bindings + set fish_key_bindings fish_vi_key_bindings # This triggers the handler, which calls us again and ensures the user_key_bindings are executed return end # The default escape timeout is 300ms. But for users of Vi bindings that can From 1f06e5f0b9ee483053b987c9cab9f1f5fce2590c Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Wed, 20 Apr 2016 23:00:54 -0700 Subject: [PATCH 135/363] add better support for IWYU and fix things Remove the "make iwyu" build target. Move the functionality into the recently introduced lint.fish script. Fix a lot, but not all, of the include-what-you-use errors. Specifically, it fixes all of the IWYU errors on my OS X server but only removes some of them on my Ubuntu 14.04 server. Fixes #2957 --- CONTRIBUTING.md | 18 +++++++ Makefile.in | 12 +---- README.md | 4 +- build_tools/iwyu.linux.imp | 17 +++++++ build_tools/iwyu.osx.imp | 97 ++++++++++++++++++++++++++++++++++++ build_tools/lint.fish | 52 +++++++++++++++---- src/autoload.cpp | 12 +++-- src/autoload.h | 2 + src/builtin.cpp | 20 ++++---- src/builtin.h | 25 ++++++++-- src/builtin_commandline.cpp | 24 ++++----- src/builtin_complete.cpp | 23 ++++----- src/builtin_jobs.cpp | 22 +++----- src/builtin_printf.cpp | 15 +++++- src/builtin_set.cpp | 22 ++++---- src/builtin_set_color.cpp | 22 ++++++-- src/builtin_string.cpp | 28 ++++++++--- src/builtin_test.cpp | 17 ++++--- src/builtin_ulimit.cpp | 13 ++--- src/color.cpp | 8 +-- src/color.h | 2 + src/common.cpp | 16 ++---- src/common.h | 17 +++---- src/complete.cpp | 10 ++-- src/complete.h | 6 +-- src/env.cpp | 11 ++-- src/env.h | 5 +- src/env_universal_common.cpp | 28 +++++------ src/env_universal_common.h | 6 ++- src/event.cpp | 12 ++--- src/event.h | 3 +- src/exec.cpp | 13 ++--- src/exec.h | 4 +- src/expand.cpp | 30 ++++++----- src/expand.h | 14 ++---- src/fallback.cpp | 38 +++++++------- src/fallback.h | 23 ++++++--- src/fish.cpp | 10 ++-- src/fish_indent.cpp | 10 ++-- src/fish_tests.cpp | 34 +++++++------ src/function.cpp | 8 ++- src/function.h | 2 +- src/highlight.cpp | 15 +++--- src/highlight.h | 10 ++-- src/history.cpp | 7 +-- src/history.h | 7 ++- src/input.cpp | 15 +++--- src/input.h | 5 +- src/input_common.cpp | 17 ++++--- src/input_common.h | 1 - src/intern.cpp | 6 +-- src/io.cpp | 8 +-- src/io.h | 7 +-- src/iothread.cpp | 12 ++--- src/iothread.h | 1 - src/key_reader.cpp | 6 +-- src/kill.cpp | 5 +- src/kill.h | 1 - src/lru.h | 2 +- src/output.cpp | 12 ++--- src/output.h | 7 +-- src/pager.cpp | 11 ++-- src/pager.h | 4 +- src/parse_constants.h | 5 +- src/parse_execution.cpp | 12 +++-- src/parse_execution.h | 3 +- src/parse_productions.cpp | 6 ++- src/parse_productions.h | 8 +-- src/parse_tree.cpp | 11 ++-- src/parse_tree.h | 7 +-- src/parse_util.cpp | 12 ++--- src/parse_util.h | 4 +- src/parser.cpp | 15 +++--- src/parser.h | 10 ++-- src/parser_keywords.cpp | 3 -- src/parser_keywords.h | 3 +- src/path.cpp | 4 +- src/path.h | 3 +- src/postfork.cpp | 9 +++- src/postfork.h | 13 ++--- src/print_help.cpp | 3 -- src/proc.cpp | 22 ++++---- src/proc.h | 13 ++--- src/reader.cpp | 18 +++---- src/reader.h | 5 +- src/sanity.cpp | 3 -- src/screen.cpp | 10 +--- src/screen.h | 2 + src/signal.cpp | 11 ++-- src/signal.h | 2 +- src/tokenizer.cpp | 6 +-- src/tokenizer.h | 3 +- src/utf8.cpp | 9 ++-- src/utf8.h | 1 - src/util.cpp | 20 ++------ src/util.h | 4 -- src/wcstringutil.cpp | 4 +- src/wcstringutil.h | 2 +- src/wgetopt.cpp | 6 +-- src/wgetopt.h | 2 +- src/wildcard.cpp | 15 ++---- src/wildcard.h | 5 +- src/wutil.cpp | 6 +-- src/wutil.h | 4 +- 104 files changed, 685 insertions(+), 533 deletions(-) create mode 100644 build_tools/iwyu.linux.imp create mode 100644 build_tools/iwyu.osx.imp diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8e610b130..c3afbd8f3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,9 +1,27 @@ + # Guidelines For Developers This document provides guidelines for making changes to the fish-shell project. This includes rules for how to format the code, naming conventions, etc. It also includes recommended best practices such as creating a Travis-CI account so you can verify your changes pass all the tests before making a pull-request. See the bottom of this document for help on installing the linting and style reformatting tools discussed in the following sections. +Fish source should limit the C++ features it uses to those available in C++03. That allows fish to use a few components from [C++TR1](https://en.wikipedia.org/wiki/C%2B%2B_Technical_Report_1) such as `shared_ptr`. It also allows fish to be built and run on OS X Snow Leopard (released in 2009); the oldest OS X release we still support. + +## Include What You Use + +You should not depend on symbols being visible to a `*.cpp` module from `#include` statements inside another header file. In other words if your module does `#include "common.h"` and that header does `#include "signal.h"` your module should pretend that sub-include is not present. It should instead directly `#include "signal.h"` if it needs any symbol from that header. That makes the actual dependencies much clearer. It also makes it easy to modify the headers included by a specific header file without having to worry that will break any module (or header) that includes a particular header. + +To help enforce this rule the `make lint` (and `make lint-all`) command will run the [include-what-you-use](http://include-what-you-use.org/) tool. The IWYU you project is on [github](https://github.com/include-what-you-use/include-what-you-use). + +To install the tool on OS X you'll need to add a [formula](https://github.com/jasonmp85/homebrew-iwyu) then install it: + +``` +brew tap jasonmp85/iwyu +brew install iwyu +``` + +On Ubuntu you can install it via `sudo apt-get install iwyu`. + ## Lint Free Code Automated analysis tools like cppcheck and oclint can point out potential bugs. They also help ensure the code has a consistent style and that it avoids patterns that tend to confuse people. diff --git a/Makefile.in b/Makefile.in index 0858c6477..b73e75d1d 100644 --- a/Makefile.in +++ b/Makefile.in @@ -859,19 +859,11 @@ depend: cp config.h /tmp/fish_make_depend/ mv $(subst obj/,/tmp/fish_make_depend/src/,$(FISH_ALL_OBJS:.o=.cpp)) /tmp/fish_make_depend/ cd /tmp/fish_make_depend && \ - makedepend -f$(CURDIR)/Makefile.in -pobj/ -Y -Isrc *.cpp + makedepend -f$(CURDIR)/Makefile.in -pobj/ -Y -Isrc *.cpp rm -Rf /tmp/fish_make_depend ./config.status .PHONY: depend -# Include What You Use -iwyu: - # Requires the --keep-going flag as it always returns 1 - # Can't set MAKEFLAGS on a target-specific basic - $(MAKE) -k _iwyu CXX=include-what-you-use -_iwyu: clean $(PROGRAMS) -.PHONY: iwyu _iwyu - # Lint the code. This only deals with C++ files. lint: build_tools/lint.fish $(CXX) $(CXXFLAGS) @@ -894,7 +886,7 @@ style-all: # Restore the source tree to the state right after extracting a tarball. distclean: clean $(MAKE) -C $(PCRE2_DIR) distclean || true - rm -f config.status config.log config.h Makefile + rm -f config.status config.log config.h Makefile osx/config.h .PHONY: distclean diff --git a/README.md b/README.md index 648e8eb63..7dd426bc8 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,9 @@ Detailed user documentation is available by running `help` within fish, and also ## Building -fish is written in a sane subset of C++98, with a few components from C++TR1. It builds successfully with g++ 4.2 or later, and with clang. It also will build as C++11. +Fish can be built using a C++11 environment but only requires C++03. It builds successfully with g++ 4.2 or later, and with clang. This allows fish to run on older systems such as OS X Snow Leopard (released in 2009). -fish can be built using autotools or Xcode. autoconf 2.60 or later is required to build from git versions, but is not required for releases. +Fish can be built using autotools or Xcode. autoconf 2.60 or later is required to build from git versions, but is not required for releases. fish depends on a curses implementation, such as ncurses. The headers and libraries are required for building. diff --git a/build_tools/iwyu.linux.imp b/build_tools/iwyu.linux.imp new file mode 100644 index 000000000..6fc614aea --- /dev/null +++ b/build_tools/iwyu.linux.imp @@ -0,0 +1,17 @@ +# Map file for the include-what-you-use tool on Linux. +[ + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "public", "", "public"] }, + { include: ["", "public", "", "public"] }, + { include: ["", "public", "", "public"] }, + { include: ["", "public", "", "public"] }, + + { symbol: ["size_t", "private", "", "public"] }, + { symbol: ["size_t", "private", "", "public"] }, + { symbol: ["size_t", "private", "", "public"] }, + { symbol: ["uint64_t", "private", "", "public"] }, +] diff --git a/build_tools/iwyu.osx.imp b/build_tools/iwyu.osx.imp new file mode 100644 index 000000000..cad467cdc --- /dev/null +++ b/build_tools/iwyu.osx.imp @@ -0,0 +1,97 @@ +# Map file for the include-what-you-use tool on OS X. For some reason +# the version installed by HomeBrew doesn't have useful mappings for the +# system provided private headers. +[ + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["<_wctype.h>", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["<_types/_intmax_t.h>", "private", "", "public"] }, + { include: ["<_types/_uintmax_t.h>", "private", "", "public"] }, + { include: ["<_types/_uint8_t.h>", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["<_types/_uint64_t.h>", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["<__functional_base>", "private", "", "public"] }, + { include: ["<__functional_base>", "private", "", "public"] }, + { include: ["<__functional_base>", "private", "", "public"] }, + { include: ["<__tree>", "private", "", "public"] }, + { include: ["<__tree>", "private", "", "public"] }, + { include: ["<_types/_uint32_t.h>", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, +# { include: ["<>", "private", "<>", "public"] }, + + { symbol: ["NULL", "private", "", "public"] }, + { symbol: ["NULL", "private", "", "public"] }, + { symbol: ["NULL", "private", "", "public"] }, + { symbol: ["NULL", "private", "", "public"] }, + { symbol: ["off_t", "private", "", "public"] }, + { symbol: ["size_t", "private", "", "public"] }, + { symbol: ["size_t", "private", "", "public"] }, + { symbol: ["size_t", "private", "", "public"] }, + { symbol: ["off_t", "private", "", "public"] }, + { symbol: ["size_t", "private", "", "public"] }, + { symbol: ["ssize_t", "private", "", "public"] }, + { symbol: ["intptr_t", "private", "", "public"] }, + { symbol: ["ssize_t", "private", "", "public"] }, + { symbol: ["gid_t", "private", "", "public"] }, + { symbol: ["uid_t", "private", "", "public"] }, + { symbol: ["pid_t", "private", "", "public"] }, + { symbol: ["pid_t", "private", "", "public"] }, + { symbol: ["uid_t", "private", "", "public"] }, + { symbol: ["gid_t", "private", "", "public"] }, + { symbol: ["uint32_t", "private", "", "public"] }, + { symbol: ["uint32_t", "private", "", "public"] }, + { symbol: ["intptr_t", "private", "", "public"] }, + { symbol: ["size_t", "private", "", "public"] }, + { symbol: ["tparm", "private", "", "public"] }, + { symbol: ["ERR", "private", "", "public"] }, + { symbol: ["select", "private", "", "public"] }, + { symbol: ["_LIBCPP_VERSION", "private", "", "public"] }, + { symbol: ["_LIBCPP_VERSION", "private", "", "public"] }, + { symbol: ["MB_CUR_MAX", "private", "", "public"] }, + { symbol: ["MB_CUR_MAX", "private", "", "public"] }, +] diff --git a/build_tools/lint.fish b/build_tools/lint.fish index 287269daa..5ac05d7d2 100755 --- a/build_tools/lint.fish +++ b/build_tools/lint.fish @@ -7,6 +7,8 @@ set cppchecks warning,performance,portability,information,missingInclude set cppcheck_args set c_files set all no +set kernel_name (uname -s) +set machine_type (uname -m) set -gx CXX $argv[1] set -e argv[1] @@ -17,15 +19,27 @@ if test "$argv[1]" = "--all" set -e argv[1] end +if test $kernel_name = Linux + # This is an awful hack. However, the include-what-you-use program spews lots of errors like + # /usr/include/unistd.h:226:10: fatal error: 'stddef.h' file not found + # if we don't explicitly tell it where to find the system headers on Linux. See + # http://stackoverflow.com/questions/19642590/libtooling-cant-find-stddef-h-nor-other-headers/ + set -l sys_includes (eval $CXX -v -c src/builtin.cpp 2>&1 | \ + sed -n -e '/^#include <...> search/,/^End of search list/s/^ *//p')[2..-2] + set -x CPLUS_INCLUDE_PATH (string join ':' $sys_includes) +end + # We only want -D and -I options to be passed thru to cppcheck. for arg in $argv if string match -q -- '-D*' $arg set cppcheck_args $cppcheck_args $arg else if string match -q -- '-I*' $arg set cppcheck_args $cppcheck_args $arg + else if string match -q -- '-iquote*' $arg + set cppcheck_args $cppcheck_args $arg end end -if test (uname -m) = "x86_64" +if test "$machine_type" = "x86_64" set cppcheck_args -D__x86_64__ -D__LP64__ $cppcheck_args end @@ -37,7 +51,7 @@ else set files (git status --porcelain --short --untracked-files=all | sed -e 's/^ *[^ ]* *//') if not set -q files[1] # No pending changes so lint the files in the most recent commit. - set files (git show --word-diff=porcelain --name-only --pretty=oneline head)[2..-1] + set files (git show --word-diff=porcelain --name-only --pretty=oneline)[2..-1] end # Extract just the C/C++ files. @@ -46,6 +60,26 @@ end # We now have a list of files to check so run the linters. if set -q c_files[1] + if type -q iwyu + # The stderr to stdout redirection is because cppcheck, incorrectly IMHO, writes its + # diagnostic messages to stderr. Anyone running this who wants to capture its output will + # expect those messages to be written to stdout. + for c_file in $c_files + echo + echo ======================================== + echo Running IWYU on $c_file + echo ======================================== + switch $kernel_name + case Darwin + include-what-you-use -Xiwyu --no_default_mappings -Xiwyu --mapping_file=build_tools/iwyu.osx.imp $cppcheck_args $c_file 2>&1 + case Linux + include-what-you-use -Xiwyu --mapping_file=build_tools/iwyu.linux.imp $cppcheck_args $c_file 2>&1 + case '*' # hope for the best + include-what-you-use $cppcheck_args $c_file 2>&1 + end + end + end + if type -q cppcheck echo echo ======================================== @@ -54,7 +88,7 @@ if set -q c_files[1] # The stderr to stdout redirection is because cppcheck, incorrectly IMHO, writes its # diagnostic messages to stderr. Anyone running this who wants to capture its output will # expect those messages to be written to stdout. - cppcheck -q --verbose --std=posix --std=c11 --language=c++ --template "[{file}:{line}]: {severity} ({id}): {message}" --suppress=missingIncludeSystem --inline-suppr --enable=$cppchecks $cppcheck_args $c_files 2>& 1 + cppcheck -q --verbose --std=posix --std=c11 --language=c++ --template "[{file}:{line}]: {severity} ({id}): {message}" --suppress=missingIncludeSystem --inline-suppr --enable=$cppchecks $cppcheck_args $c_files 2>&1 end if type -q oclint @@ -65,25 +99,25 @@ if set -q c_files[1] # The stderr to stdout redirection is because oclint, incorrectly writes its final summary # counts of the errors detected to stderr. Anyone running this who wants to capture its # output will expect those messages to be written to stdout. - if test (uname -s) = "Darwin" + if test "$kernel_name" = "Darwin" if not test -f compile_commands.json - xcodebuild > xcodebuild.log - oclint-xcodebuild xcodebuild.log > /dev/null + xcodebuild >xcodebuild.log + oclint-xcodebuild xcodebuild.log >/dev/null end if test $all = yes - oclint-json-compilation-database -e '/pcre2-10.21/' -- -enable-global-analysis 2>& 1 + oclint-json-compilation-database -e '/pcre2-10.21/' -- -enable-global-analysis 2>&1 else set i_files for f in $c_files set i_files $i_files -i $f end echo oclint-json-compilation-database -e '/pcre2-10.21/' $i_files - oclint-json-compilation-database -e '/pcre2-10.21/' $i_files 2>& 1 + oclint-json-compilation-database -e '/pcre2-10.21/' $i_files 2>&1 end else # Presumably we're on Linux or other platform not requiring special # handling for oclint to work. - oclint $c_files -- $argv 2>& 1 + oclint $c_files -- $argv 2>&1 end end else diff --git a/src/autoload.cpp b/src/autoload.cpp index ae63335aa..6f7c39058 100644 --- a/src/autoload.cpp +++ b/src/autoload.cpp @@ -1,5 +1,4 @@ // The classes responsible for autoloading functions and completions. -#include "autoload.h" #include #include #include @@ -9,12 +8,17 @@ #include #include #include +#include +#include +#include +#include +#include + +#include "autoload.h" #include "common.h" -#include "config.h" // IWYU pragma: keep #include "env.h" #include "exec.h" -#include "signal.h" // IWYU pragma: keep - needed for CHECK_BLOCK -#include "wutil.h" +#include "wutil.h" // IWYU pragma: keep // The time before we'll recheck an autoloaded file. static const int kAutoloadStalenessInterval = 15; diff --git a/src/autoload.h b/src/autoload.h index 5f8b4d310..5b1d5dfe4 100644 --- a/src/autoload.h +++ b/src/autoload.h @@ -6,6 +6,8 @@ #include #include #include +#include + #include "common.h" #include "lru.h" diff --git a/src/builtin.cpp b/src/builtin.cpp index a2bf6736e..b7a84c693 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -15,12 +15,10 @@ // Check the other builtin manuals for proper syntax. // // 4). Use 'git add doc_src/NAME.txt' to start tracking changes to the documentation file. -#include "config.h" // IWYU pragma: keep #include #include #include -#include #include #include #include @@ -31,12 +29,13 @@ #include #include #include -#include #include #include +#include +#include // IWYU pragma: keep +#include #include "fallback.h" // IWYU pragma: keep - #include "builtin.h" #include "complete.h" #include "env.h" @@ -49,7 +48,6 @@ #include "input.h" #include "intern.h" #include "parse_constants.h" -#include "parse_tree.h" #include "parse_util.h" #include "parser.h" #include "parser_keywords.h" @@ -61,6 +59,8 @@ #include "wcstringutil.h" #include "wgetopt.h" #include "wutil.h" +#include "common.h" +#include "io.h" // The default prompt for the read command. #define DEFAULT_READ_PROMPT L"set_color green; echo -n read; set_color normal; echo -n \"> \"" @@ -103,7 +103,7 @@ int builtin_count_args(const wchar_t *const *argv) { /// This function works like wperror, but it prints its result into the streams.err string instead /// to stderr. Used by the builtin commands. -static void builtin_wperror(const wchar_t *s, io_streams_t &streams) { +void builtin_wperror(const wchar_t *s, io_streams_t &streams) { char *err = strerror(errno); if (s != NULL) { streams.err.append(s); @@ -228,15 +228,15 @@ void builtin_print_help(parser_t &parser, io_streams_t &streams, const wchar_t * } /// Perform error reporting for encounter with unknown option. -static void builtin_unknown_option(parser_t &parser, io_streams_t &streams, const wchar_t *cmd, - const wchar_t *opt) { +void builtin_unknown_option(parser_t &parser, io_streams_t &streams, const wchar_t *cmd, + const wchar_t *opt) { streams.err.append_format(BUILTIN_ERR_UNKNOWN, cmd, opt); builtin_print_help(parser, streams, cmd, streams.err); } /// Perform error reporting for encounter with missing argument. -static void builtin_missing_argument(parser_t &parser, io_streams_t &streams, const wchar_t *cmd, - const wchar_t *opt) { +void builtin_missing_argument(parser_t &parser, io_streams_t &streams, const wchar_t *cmd, + const wchar_t *opt) { streams.err.append_format(BUILTIN_ERR_MISSING, cmd, opt); builtin_print_help(parser, streams, cmd, streams.err); } diff --git a/src/builtin.h b/src/builtin.h index cea7318ad..0d62babe5 100644 --- a/src/builtin.h +++ b/src/builtin.h @@ -2,14 +2,15 @@ #ifndef FISH_BUILTIN_H #define FISH_BUILTIN_H -#include // for size_t -#include // for vector +#include +#include #include "common.h" -#include "io.h" class completion_t; class parser_t; +class output_stream_t; +struct io_streams_t; enum { COMMAND_NOT_BUILTIN, BUILTIN_REGULAR, BUILTIN_FUNCTION }; @@ -97,4 +98,22 @@ wcstring builtin_help_get(parser_t &parser, const wchar_t *cmd); int define_function(parser_t &parser, io_streams_t &streams, const wcstring_list_t &c_args, const wcstring &contents, int definition_line_offset, wcstring *out_err); +// Print help for the specified builtin. If \c b is sb_err, also print the line information. +void builtin_print_help(parser_t &parser, io_streams_t &streams, const wchar_t *cmd, + output_stream_t &b); + +// Counts the number of non null pointers in the specified array. +int builtin_count_args(const wchar_t *const *argv); + +// Perform error reporting for encounter with unknown option. +void builtin_unknown_option(parser_t &parser, io_streams_t &streams, const wchar_t *cmd, + const wchar_t *opt); + +// Perform error reporting for encounter with missing argument. +void builtin_missing_argument(parser_t &parser, io_streams_t &streams, const wchar_t *cmd, + const wchar_t *opt); + +// This function works like wperror, but it prints its result into the streams.err string instead +// to stderr. Used by the builtin commands. +void builtin_wperror(const wchar_t *s, io_streams_t &streams); #endif diff --git a/src/builtin_commandline.cpp b/src/builtin_commandline.cpp index 122681af3..e84bcb073 100644 --- a/src/builtin_commandline.cpp +++ b/src/builtin_commandline.cpp @@ -1,33 +1,29 @@ /** \file builtin_commandline.c Functions defining the commandline builtin Functions used for implementing the commandline builtin. - */ -#include "config.h" - #include -#include #include -#include -#include -#include -#include +#include +#include +#include +#include +#include -#include "fallback.h" +#include "fallback.h" // IWYU pragma: keep #include "util.h" - -#include "wutil.h" #include "builtin.h" #include "common.h" #include "wgetopt.h" #include "reader.h" #include "proc.h" -#include "parser.h" #include "tokenizer.h" -#include "input_common.h" #include "input.h" - #include "parse_util.h" +#include "io.h" +#include "wutil.h" // IWYU pragma: keep + +class parser_t; /** Which part of the comandbuffer are we operating on diff --git a/src/builtin_complete.cpp b/src/builtin_complete.cpp index f7f3f256d..bd117a8fa 100644 --- a/src/builtin_complete.cpp +++ b/src/builtin_complete.cpp @@ -1,28 +1,27 @@ /** \file builtin_complete.c Functions defining the complete builtin Functions used for implementing the complete builtin. - */ -#include "config.h" - #include -#include #include -#include -#include -#include -#include +#include +#include +#include // IWYU pragma: keep +#include -#include "fallback.h" -#include "util.h" - -#include "wutil.h" +#include "fallback.h" // IWYU pragma: keep #include "builtin.h" #include "common.h" #include "complete.h" #include "wgetopt.h" #include "parser.h" #include "reader.h" +#include "env.h" +#include "io.h" +#include "parse_constants.h" +#include "proc.h" +#include "parse_util.h" +#include "wutil.h" // IWYU pragma: keep /* builtin_complete_* are a set of rather silly looping functions that diff --git a/src/builtin_jobs.cpp b/src/builtin_jobs.cpp index 9cad7d551..419827693 100644 --- a/src/builtin_jobs.cpp +++ b/src/builtin_jobs.cpp @@ -1,29 +1,23 @@ /** \file builtin_jobs.c Functions for executing the jobs builtin. */ -#include "config.h" +#include "config.h" // IWYU pragma: keep -#include -#include -#include -#include -#include #include -#include -#include -#include -#include - -#include "fallback.h" -#include "util.h" +#include +#ifdef HAVE__PROC_SELF_STAT +#include +#endif +#include "fallback.h" // IWYU pragma: keep #include "wutil.h" #include "builtin.h" #include "proc.h" -#include "parser.h" #include "common.h" #include "wgetopt.h" +#include "io.h" +class parser_t; /** Print modes for the jobs builtin diff --git a/src/builtin_printf.cpp b/src/builtin_printf.cpp index d5fa1d638..97427948f 100644 --- a/src/builtin_printf.cpp +++ b/src/builtin_printf.cpp @@ -49,11 +49,24 @@ /* This file has been imported from source code of printf command in GNU Coreutils version 6.9 */ +#include +#include +#include +#include +#include #include #include -#include +#include +#include +#include #include "common.h" +#include "io.h" +#include "wutil.h" // IWYU pragma: keep +#include "proc.h" +#include "builtin.h" + +class parser_t; struct builtin_printf_state_t { diff --git a/src/builtin_set.cpp b/src/builtin_set.cpp index bd871b09d..823bdebc7 100644 --- a/src/builtin_set.cpp +++ b/src/builtin_set.cpp @@ -3,28 +3,30 @@ Functions used for implementing the set builtin. */ -#include "config.h" - #include -#include #include #include -#include -#include -#include #include #include -#include "fallback.h" -#include "util.h" +#include +#include +#include +#include +#include +#include +#include -#include "wutil.h" +#include "fallback.h" // IWYU pragma: keep +#include "wutil.h" // IWYU pragma: keep #include "builtin.h" #include "env.h" #include "expand.h" #include "common.h" #include "wgetopt.h" #include "proc.h" -#include "parser.h" +#include "io.h" + +class parser_t; /** Error message for invalid path operations diff --git a/src/builtin_set_color.cpp b/src/builtin_set_color.cpp index 449e38954..d3b744cd9 100644 --- a/src/builtin_set_color.cpp +++ b/src/builtin_set_color.cpp @@ -5,10 +5,6 @@ Functions used for implementing the set_color builtin. */ #include "config.h" -#include "builtin.h" -#include "color.h" -#include "output.h" - #if HAVE_NCURSES_H #include #elif HAVE_NCURSES_CURSES_H @@ -16,13 +12,29 @@ Functions used for implementing the set_color builtin. #else #include #endif - #if HAVE_TERM_H #include #elif HAVE_NCURSES_TERM_H #include #endif +#include +#include +#include +#include +#include +#include +#include +#include "builtin.h" +#include "color.h" +#include "output.h" +#include "wgetopt.h" +#include "proc.h" +#include "io.h" +#include "common.h" +#include "wutil.h" // IWYU pragma: keep + +class parser_t; /** Error message for invalid path operations diff --git a/src/builtin_string.cpp b/src/builtin_string.cpp index 10508a710..9efa6c2f7 100644 --- a/src/builtin_string.cpp +++ b/src/builtin_string.cpp @@ -1,25 +1,37 @@ /** \file builtin_string.cpp Implementation of the string builtin. */ - -#include "config.h" // IWYU pragma: keep +#include "config.h" #define PCRE2_CODE_UNIT_WIDTH WCHAR_T_BITS #ifdef _WIN32 #define PCRE2_STATIC #endif -#include "pcre2.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pcre2.h" #include "builtin.h" #include "common.h" -#include "parser.h" #include "parse_util.h" #include "wgetopt.h" #include "wildcard.h" -#include "wutil.h" -#include -#include -#include +#include "fallback.h" // IWYU pragma: keep +#include "io.h" +#include "wutil.h" // IWYU pragma: keep + +class parser_t; #define MAX_REPLACE_SIZE size_t(1048576) // pcre2_substitute maximum output size in wchar_t #define STRING_ERR_MISSING _(L"%ls: Expected argument\n") diff --git a/src/builtin_test.cpp b/src/builtin_test.cpp index 00fb88601..9c66238dc 100644 --- a/src/builtin_test.cpp +++ b/src/builtin_test.cpp @@ -4,17 +4,22 @@ Functions used for implementing the test builtin. Implemented from scratch (yes, really) by way of IEEE 1003.1 as reference. */ - -#include "config.h" -#include "common.h" -#include "builtin.h" -#include "wutil.h" -#include "proc.h" #include #include #include #include +#include +#include +#include +#include +#include +#include +#include "common.h" +#include "builtin.h" +#include "wutil.h" // IWYU pragma: keep +#include "proc.h" +#include "io.h" enum { diff --git a/src/builtin_ulimit.cpp b/src/builtin_ulimit.cpp index 3b2dadd65..270360233 100644 --- a/src/builtin_ulimit.cpp +++ b/src/builtin_ulimit.cpp @@ -3,24 +3,19 @@ Functions used for implementing the ulimit builtin. */ -#include "config.h" - -#include -#include #include -#include -#include #include -#include #include -#include "fallback.h" +#include "fallback.h" // IWYU pragma: keep #include "util.h" - #include "builtin.h" #include "common.h" #include "wgetopt.h" +#include "io.h" +#include "wutil.h" // IWYU pragma: keep +class parser_t; /** Struct describing a resource limit diff --git a/src/color.cpp b/src/color.cpp index 4c3c89d21..6ff52e352 100644 --- a/src/color.cpp +++ b/src/color.cpp @@ -1,11 +1,11 @@ // Color class implementation. -#include "color.h" -#include "fallback.h" // IWYU pragma: keep #include #include #include -#include -#include + +#include "color.h" +#include "fallback.h" // IWYU pragma: keep +#include "common.h" bool rgb_color_t::try_parse_special(const wcstring &special) { diff --git a/src/color.h b/src/color.h index 1faa35a6b..881ce4b38 100644 --- a/src/color.h +++ b/src/color.h @@ -4,6 +4,8 @@ #include #include +#include + #include "common.h" /* 24 bit color */ diff --git a/src/common.cpp b/src/common.cpp index 545288fc0..3f738ec05 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -3,16 +3,12 @@ Various functions, mostly string utilities, that are used by most parts of fish. */ - #include "config.h" - #include - #ifdef HAVE_SIGINFO_H #include #endif - #include #include #include @@ -22,28 +18,26 @@ parts of fish. #include #include #include - - #ifdef HAVE_SYS_IOCTL_H #include #endif - #include #include #include #include #include +#include #include #include #include - #ifdef HAVE_EXECINFO_H #include #endif +#include +#include -#include "fallback.h" - -#include "wutil.h" +#include "fallback.h" // IWYU pragma: keep +#include "wutil.h" // IWYU pragma: keep #include "common.h" #include "expand.h" #include "wildcard.h" diff --git a/src/common.h b/src/common.h index d6eb01910..dc6e02366 100644 --- a/src/common.h +++ b/src/common.h @@ -1,29 +1,26 @@ /** \file common.h Prototypes for various functions, mostly string utilities, that are used by most parts of fish. */ - #ifndef FISH_COMMON_H -/** - Header guard -*/ #define FISH_COMMON_H +#include "config.h" #include #include #include -#include #include #include #include #include #include -#include -#include #include - #include -#include "config.h" -#include "fallback.h" +#include +#include +#include + +#include "fallback.h" // IWYU pragma: keep +#include "signal.h" // IWYU pragma: keep /** Avoid writing the type name twice in a common "static_cast-initialization". diff --git a/src/complete.cpp b/src/complete.cpp index 5c817b8d0..db63e3487 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -2,10 +2,7 @@ These functions are used for storing and retrieving tab-completion data, as well as for performing tab-completion. */ -#include "config.h" - #include -#include #include #include #include @@ -17,10 +14,11 @@ #include #include #include +#include +#include -#include "fallback.h" // IWYU pragma: keep +#include "fallback.h" // IWYU pragma: keep #include "util.h" - #include "wildcard.h" #include "proc.h" #include "parser.h" @@ -32,7 +30,7 @@ #include "expand.h" #include "common.h" #include "parse_util.h" -#include "wutil.h" +#include "wutil.h" // IWYU pragma: keep #include "path.h" #include "parse_tree.h" #include "iothread.h" diff --git a/src/complete.h b/src/complete.h index 6cbb13bc2..1e2c1a185 100644 --- a/src/complete.h +++ b/src/complete.h @@ -6,16 +6,14 @@ */ #ifndef FISH_COMPLETE_H - -/** - Header guard -*/ #define FISH_COMPLETE_H #include #include +#include #include "common.h" + /** * Use all completions */ diff --git a/src/env.cpp b/src/env.cpp index 56c9deb07..22d4c5734 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -1,8 +1,6 @@ /** \file env.c Functions for setting and getting environment variables. */ -#include "config.h" // IWYU pragma: keep - #include #include #include @@ -19,10 +17,11 @@ #include #include #include +#include +#include -#include "fallback.h" - -#include "wutil.h" +#include "fallback.h" // IWYU pragma: keep +#include "wutil.h" // IWYU pragma: keep #include "proc.h" #include "common.h" #include "env.h" @@ -34,8 +33,8 @@ #include "input.h" #include "event.h" #include "path.h" - #include "fish_version.h" +#include "input_common.h" /** Value denoting a null string */ #define ENV_NULL L"\x1d" diff --git a/src/env.h b/src/env.h index 3eb6313f9..4b14d548c 100644 --- a/src/env.h +++ b/src/env.h @@ -1,14 +1,15 @@ /** \file env.h Prototypes for functions for setting and getting environment variables. */ - #ifndef FISH_ENV_H #define FISH_ENV_H -#include #include #include #include +#include +#include +#include #include "common.h" diff --git a/src/env_universal_common.cpp b/src/env_universal_common.cpp index 900a2d75a..3964a4d84 100644 --- a/src/env_universal_common.cpp +++ b/src/env_universal_common.cpp @@ -3,46 +3,46 @@ The utility library for universal variables. Used both by the client library and by the daemon. - */ #include "config.h" -#include "env_universal_common.h" - #include -#include #include -#include -#include -#include // IWYU pragma: keep - needed for htonl +#include // IWYU pragma: keep #include #include #include #include #include -#include -#include #include #include #include -#include #include #include #include #include -#include -#include - +#include +#include +#include #ifdef HAVE_SYS_SELECT_H #include #endif +#include +#include +#include +// We need the ioctl.h header so we can check if SIOCGIFHWADDR is defined by it so we know if we're +// on a Linux system. +#include // IWYU pragma: keep +// We need the sys/file.h for the flock() declaration on Linux but not OS X. +#include // IWYU pragma: keep +#include "env_universal_common.h" #include "fallback.h" // IWYU pragma: keep #include "util.h" - #include "common.h" #include "wutil.h" #include "utf8.h" +#include "env.h" #if __APPLE__ #define FISH_NOTIFYD_AVAILABLE 1 diff --git a/src/env_universal_common.h b/src/env_universal_common.h index edc360167..8e319fe1c 100644 --- a/src/env_universal_common.h +++ b/src/env_universal_common.h @@ -1,11 +1,13 @@ #ifndef FISH_ENV_UNIVERSAL_COMMON_H #define FISH_ENV_UNIVERSAL_COMMON_H -#include -#include #include #include +#include +#include +#include #include + #include "common.h" #include "wutil.h" #include "env.h" diff --git a/src/event.cpp b/src/event.cpp index eeff3eb5d..ee1d7c0a9 100644 --- a/src/event.cpp +++ b/src/event.cpp @@ -1,18 +1,17 @@ /** \file event.c Functions for handling event triggers - */ -#include "config.h" // IWYU pragma: keep - #include -#include #include -#include +#include +#include +#include #include +#include #include "fallback.h" // IWYU pragma: keep -#include "wutil.h" // IWYU pragma: keep - needed for gettext +#include "wutil.h" // IWYU pragma: keep #include "input_common.h" #include "proc.h" #include "parser.h" @@ -21,7 +20,6 @@ #include "signal.h" #include "io.h" - /** Number of signals that can be queued before an overflow occurs */ diff --git a/src/event.h b/src/event.h index bf068c5d3..93051fac4 100644 --- a/src/event.h +++ b/src/event.h @@ -12,8 +12,9 @@ #ifndef FISH_EVENT_H #define FISH_EVENT_H -#include +#include #include +#include #include "common.h" diff --git a/src/exec.cpp b/src/exec.cpp index c9e9833e2..02d1b7244 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -4,8 +4,7 @@ Some of the code in this file is based on code from the Glibc manual, though the changes performed have been massive. */ - -#include "config.h" // IWYU pragma: keep +#include "config.h" #include #include @@ -24,17 +23,16 @@ #include #include #include -#include // IWYU pragma: keep - suggests instead -#include - +#include #ifdef HAVE_SIGINFO_H #include #endif +#include -#include "fallback.h" // IWYU pragma: keep +#include "fallback.h" // IWYU pragma: keep #include "postfork.h" #include "common.h" -#include "wutil.h" +#include "wutil.h" // IWYU pragma: keep #include "proc.h" #include "exec.h" #include "parser.h" @@ -42,7 +40,6 @@ #include "function.h" #include "env.h" #include "signal.h" -#include "parse_util.h" #include "io.h" #include "parse_tree.h" #include "reader.h" diff --git a/src/exec.h b/src/exec.h index 41dc31937..f2d6c8e7b 100644 --- a/src/exec.h +++ b/src/exec.h @@ -3,13 +3,11 @@ */ #ifndef FISH_EXEC_H -/** - Header guard -*/ #define FISH_EXEC_H #include #include +#include #include "common.h" diff --git a/src/expand.cpp b/src/expand.cpp index 72a72e77f..1ac626e96 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -4,11 +4,10 @@ String expansion functions. These functions perform several kinds of parameter expansion. */ - -#include "config.h" // IWYU pragma: keep +// IWYU pragma: no_include +#include "config.h" #include -#include #include #include #include @@ -16,38 +15,43 @@ parameter expansion. #include #include #include -#include -#include #include #include #ifdef HAVE_SYS_SYSCTL_H -#include // IWYU pragma: keep - needed for KERN_PROCARGS2 +#include // IWYU pragma: keep #endif - #include #include - #ifdef SunOS #include #endif +#include // IWYU pragma: keep +#include +#if __APPLE__ +#include +#else +#include +#include +#endif #include "fallback.h" // IWYU pragma: keep #include "util.h" - #include "common.h" -#include "wutil.h" +#include "wutil.h" // IWYU pragma: keep #include "env.h" #include "proc.h" -#include "parser.h" #include "path.h" #include "expand.h" #include "wildcard.h" #include "exec.h" -#include "tokenizer.h" #include "complete.h" #include "iothread.h" - #include "parse_util.h" +#include "parse_constants.h" +#ifdef KERN_PROCARGS2 +#else +#include "tokenizer.h" +#endif /** Description for child process diff --git a/src/expand.h b/src/expand.h index fafbcd2ad..d0b5e38b0 100644 --- a/src/expand.h +++ b/src/expand.h @@ -6,20 +6,16 @@ benefit from using a more clever memory allocation scheme, perhaps an evil combination of talloc, string buffers and reference counting. - */ - #ifndef FISH_EXPAND_H -/** - Header guard -*/ #define FISH_EXPAND_H -#include "config.h" // for __warn_unused +#include "config.h" -#include -#include // for string -#include // for vector +#include +#include +#include +#include #include "common.h" #include "parse_constants.h" diff --git a/src/fallback.cpp b/src/fallback.cpp index ed29f5de9..e3dcfed0b 100644 --- a/src/fallback.cpp +++ b/src/fallback.cpp @@ -7,46 +7,44 @@ incomplete. lrand28_r internally uses the regular (bad) rand_r function, the gettext function doesn't actually do anything, etc. */ - #include "config.h" - +// IWYU likes to recommend adding term.h when we want ncurses.h. +// IWYU pragma: no_include term.h #include -#include +#include // IWYU pragma: keep #include -#include -#include -#include -#include +#include // IWYU pragma: keep +#include // IWYU pragma: keep +#include // IWYU pragma: keep +#include // IWYU pragma: keep #include #include #include -#include -#include -#include -#include - +#include // IWYU pragma: keep +#include // IWYU pragma: keep +#include // IWYU pragma: keep +#include // IWYU pragma: keep #if HAVE_GETTEXT #include #endif - #if HAVE_NCURSES_H -#include +#include // IWYU pragma: keep #elif HAVE_NCURSES_CURSES_H #include #else #include #endif - #if HAVE_TERM_H -#include +#include // IWYU pragma: keep #elif HAVE_NCURSES_TERM_H #include #endif +#include // IWYU pragma: keep +#include // IWYU pragma: keep -#include "fallback.h" -#include "util.h" - +#include "fallback.h" // IWYU pragma: keep +#include "util.h" // IWYU pragma: keep #ifndef HAVE___ENVIRON @@ -1339,8 +1337,6 @@ int fish_wcswidth(const wchar_t *str, size_t n) * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c */ -#include - struct interval { int first; diff --git a/src/fallback.h b/src/fallback.h index 0623ec54a..ae48e2324 100644 --- a/src/fallback.h +++ b/src/fallback.h @@ -1,16 +1,23 @@ - #ifndef FISH_FALLBACK_H #define FISH_FALLBACK_H -#include +#include "config.h" + +// IWYU likes to recommend adding when we want . If we add it breaks +// compiling several modules that include this header because they use symbols which are defined as +// macros in . +// IWYU pragma: no_include #include -#include -#include -#include -#include -#include -#include #include +#include +// The following include must be kept despite what IWYU says. That's because of the interaction +// between the weak linking of `wcsdup` and `wcscasecmp` via `#define`s below and the declarations +// in . At least on OS X if we don't do this we get compilation errors do to the macro +// substitution if wchar.h is included after this header. +#include // IWYU pragma: keep +#if HAVE_NCURSES_H +#include // IWYU pragma: keep +#endif /** fish's internal versions of wcwidth and wcswidth, which can use an internal implementation if the system one is busted. */ int fish_wcwidth(wchar_t wc); diff --git a/src/fish.cpp b/src/fish.cpp index 9676814de..128a529c4 100644 --- a/src/fish.cpp +++ b/src/fish.cpp @@ -15,17 +15,14 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ - /** \file fish.c The main loop of fish. */ - #include "config.h" #include #include #include -#include #include #include #include @@ -37,22 +34,23 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA #include #include #include -#include // IWYU pragma: keep - suggests internal header +#include // IWYU pragma: keep #include #include #include +#include +#include #include "fallback.h" // IWYU pragma: keep #include "common.h" #include "reader.h" #include "builtin.h" #include "function.h" -#include "wutil.h" +#include "wutil.h" // IWYU pragma: keep #include "env.h" #include "proc.h" #include "parser.h" #include "expand.h" -#include "intern.h" #include "event.h" #include "history.h" #include "path.h" diff --git a/src/fish_indent.cpp b/src/fish_indent.cpp index a469ccf76..fbe9b13cd 100644 --- a/src/fish_indent.cpp +++ b/src/fish_indent.cpp @@ -1,3 +1,4 @@ +// The fish_indent program. /* Copyright (C) 2014 ridiculous_fish @@ -14,10 +15,6 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ - -// The fish_indent proegram. -#include "config.h" - #include #include #include @@ -27,11 +24,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA #include #include #include +#include +#include +#include #include "color.h" #include "highlight.h" #include "parse_constants.h" -#include "wutil.h" +#include "wutil.h" // IWYU pragma: keep #include "common.h" #include "output.h" #include "env.h" diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index ef1956cca..b616ec518 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -1,21 +1,14 @@ /** \file fish_tests.c Various bug and feature tests. Compiled and run by make test. */ - -#include "config.h" - +// IWYU pragma: no_include #include #include #include #include #include -#include -#include -#include #include -#include #include -#include #include #include #include @@ -25,31 +18,36 @@ #include #include #include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -#include "fallback.h" +#include "fallback.h" // IWYU pragma: keep #include "util.h" #include "common.h" #include "proc.h" #include "reader.h" #include "builtin.h" #include "function.h" -#include "autoload.h" #include "complete.h" -#include "wutil.h" +#include "wutil.h" // IWYU pragma: keep #include "env.h" #include "expand.h" #include "parser.h" #include "tokenizer.h" -#include "output.h" -#include "exec.h" #include "event.h" #include "path.h" #include "history.h" #include "highlight.h" #include "iothread.h" -#include "postfork.h" #include "signal.h" #include "parse_tree.h" #include "parse_util.h" @@ -59,6 +57,12 @@ #include "utf8.h" #include "env_universal_common.h" #include "wcstringutil.h" +#include "color.h" +#include "io.h" +#include "lru.h" +#include "parse_constants.h" +#include "screen.h" +#include "input_common.h" static const char * const * s_arguments; static int s_test_run_count = 0; diff --git a/src/function.cpp b/src/function.cpp index 711820302..4d731ff3b 100644 --- a/src/function.cpp +++ b/src/function.cpp @@ -6,9 +6,7 @@ is taken care of by the parser and to some degree the builtin handling library. */ - -#include "config.h" // IWYU pragma: keep - +// IWYU pragma: no_include #include #include #include @@ -17,10 +15,10 @@ #include #include #include +#include -#include "wutil.h" +#include "wutil.h" // IWYU pragma: keep #include "fallback.h" // IWYU pragma: keep - #include "autoload.h" #include "function.h" #include "common.h" diff --git a/src/function.h b/src/function.h index 5a993fec8..8ac45a11f 100644 --- a/src/function.h +++ b/src/function.h @@ -6,12 +6,12 @@ is taken care of by the parser and to some degree the builtin handling library. */ - #ifndef FISH_FUNCTION_H #define FISH_FUNCTION_H #include #include +#include #include "common.h" #include "event.h" diff --git a/src/highlight.cpp b/src/highlight.cpp index 3014ebdfa..827a324c3 100644 --- a/src/highlight.cpp +++ b/src/highlight.cpp @@ -1,9 +1,7 @@ /** \file highlight.c Functions for syntax highlighting */ - -#include "config.h" // IWYU pragma: keep - +// IWYU pragma: no_include #include #include #include @@ -13,10 +11,13 @@ #include #include #include +#include +#include +#include +#include -#include "fallback.h" - -#include "wutil.h" +#include "fallback.h" // IWYU pragma: keep +#include "wutil.h" // IWYU pragma: keep #include "highlight.h" #include "tokenizer.h" #include "parse_util.h" @@ -26,12 +27,12 @@ #include "env.h" #include "expand.h" #include "common.h" -#include "complete.h" #include "output.h" #include "wildcard.h" #include "path.h" #include "history.h" #include "parse_tree.h" +#include "color.h" #define CURSOR_POSITION_INVALID ((size_t)(-1)) diff --git a/src/highlight.h b/src/highlight.h index 161b4b737..46149f464 100644 --- a/src/highlight.h +++ b/src/highlight.h @@ -5,12 +5,12 @@ #ifndef FISH_HIGHLIGHT_H #define FISH_HIGHLIGHT_H -#include // for assert -#include // for size_t -#include // for uint32_t -#include // for vector +#include +#include +#include +#include -#include "common.h" // for wcstring, wcstring_list_t +#include "common.h" #include "env.h" #include "color.h" diff --git a/src/history.cpp b/src/history.cpp index bc9ddd3ed..dfb3608c1 100644 --- a/src/history.cpp +++ b/src/history.cpp @@ -1,5 +1,4 @@ // History functions, part of the user interface. -#include "history.h" #include #include #include @@ -16,10 +15,12 @@ #include #include #include +#include + #include "common.h" -#include "config.h" #include "env.h" #include "fallback.h" // IWYU pragma: keep +#include "history.h" #include "iothread.h" #include "lru.h" #include "parse_constants.h" @@ -28,7 +29,7 @@ #include "reader.h" #include "sanity.h" #include "signal.h" -#include "wutil.h" +#include "wutil.h" // IWYU pragma: keep // Our history format is intended to be valid YAML. Here it is: // diff --git a/src/history.h b/src/history.h index b278c3ccf..a861f9784 100644 --- a/src/history.h +++ b/src/history.h @@ -2,8 +2,8 @@ #ifndef FISH_HISTORY_H #define FISH_HISTORY_H +// IWYU pragma: no_include #include -#include #include #include #include @@ -12,8 +12,11 @@ #include #include #include +#include +#include + #include "common.h" -#include "wutil.h" +#include "wutil.h" // IWYU pragma: keep // Fish supports multiple shells writing to history at once. Here is its strategy: // diff --git a/src/input.cpp b/src/input.cpp index b4277724b..c45939bc4 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -1,16 +1,13 @@ /** \file input.c Functions for reading a character of input from stdin. - */ - #include "config.h" #include #include #include #include - #if HAVE_NCURSES_H #include #elif HAVE_NCURSES_CURSES_H @@ -18,17 +15,19 @@ #else #include #endif - #if HAVE_TERM_H #include #elif HAVE_NCURSES_TERM_H #include #endif - #include +#include +#include +#include +#include #include "fallback.h" // IWYU pragma: keep -#include "wutil.h" // IWYU pragma: keep - needed for wgettext +#include "wutil.h" // IWYU pragma: keep #include "reader.h" #include "proc.h" #include "common.h" @@ -37,11 +36,9 @@ #include "parser.h" #include "env.h" #include "event.h" -#include "signal.h" // IWYU pragma: keep - needed for CHECK_BLOCK +#include "signal.h" // IWYU pragma: keep #include "io.h" #include "output.h" -#include -#include #define DEFAULT_TERM L"ansi" #define MAX_INPUT_FUNCTION_ARGS 20 diff --git a/src/input.h b/src/input.h index 47315c1c4..e3e41936d 100644 --- a/src/input.h +++ b/src/input.h @@ -2,19 +2,16 @@ Functions for reading a character of input from stdin, using the inputrc information for key bindings. - */ - #ifndef FISH_INPUT_H #define FISH_INPUT_H #include -#include #include +#include #include "common.h" #include "env.h" -#include "input_common.h" #define DEFAULT_BIND_MODE L"default" #define FISH_BIND_MODE_VAR L"fish_bind_mode" diff --git a/src/input_common.cpp b/src/input_common.cpp index 7c018c2d1..3584f903d 100644 --- a/src/input_common.cpp +++ b/src/input_common.cpp @@ -1,27 +1,30 @@ /** \file input_common.c Implementation file for the low level input library - */ #include "config.h" - #include #include -#include #include #include #include -#include // for wint_t -#include // for deque -#include // for swap, pair +#include +#include #ifdef HAVE_SYS_SELECT_H #include #endif +#include +#include +#include +#include +#include +#include +#include +#include #include "fallback.h" // IWYU pragma: keep #include "util.h" - #include "common.h" #include "input_common.h" #include "env_universal_common.h" diff --git a/src/input_common.h b/src/input_common.h index 8f4cdd52a..1f54317a4 100644 --- a/src/input_common.h +++ b/src/input_common.h @@ -1,7 +1,6 @@ /** \file input_common.h Header file for the low level input library - */ #ifndef INPUT_COMMON_H #define INPUT_COMMON_H diff --git a/src/intern.cpp b/src/intern.cpp index 2ec0af8a6..0724fcd03 100644 --- a/src/intern.cpp +++ b/src/intern.cpp @@ -1,14 +1,14 @@ /** \file intern.c Library for pooling common strings - */ -#include "config.h" // IWYU pragma: keep - #include #include #include #include +#include +#include +#include #include "fallback.h" // IWYU pragma: keep #include "common.h" diff --git a/src/io.cpp b/src/io.cpp index 614528305..ed5afe557 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -1,23 +1,19 @@ /** \file io.c Utilities for io redirection. - */ -#include "config.h" // IWYU pragma: keep - - #include #include #include #include +#include #include "fallback.h" // IWYU pragma: keep -#include "wutil.h" +#include "wutil.h" // IWYU pragma: keep #include "exec.h" #include "common.h" #include "io.h" - io_data_t::~io_data_t() { } diff --git a/src/io.h b/src/io.h index 181099adf..ed634f192 100644 --- a/src/io.h +++ b/src/io.h @@ -4,10 +4,10 @@ #include #include #include - +#include // Note that we have to include something to get any _LIBCPP_VERSION defined so we can detect libc++ -// So it's key that vector go above. If we didn't need vector for other reasons, we might include ciso646, which does nothing - +// So it's key that vector go above. If we didn't need vector for other reasons, we might include +// ciso646, which does nothing #if defined(_LIBCPP_VERSION) || __cplusplus > 199711L // C++11 or libc++ (which is a C++11-only library, but the memory header works OK in C++03) #include @@ -17,6 +17,7 @@ using std::shared_ptr; #include using std::tr1::shared_ptr; #endif +#include #include "common.h" diff --git a/src/iothread.cpp b/src/iothread.cpp index 7a37b63bc..3623850e3 100644 --- a/src/iothread.cpp +++ b/src/iothread.cpp @@ -1,17 +1,17 @@ -#include "config.h" // IWYU pragma: keep -#include "iothread.h" -#include "common.h" #include #include #include -#include // IWYU pragma: keep - for _POSIX_THREADS_MAX, suggests internal header +#include #include -#include +#include #include #include #include #include -#include +#include + +#include "iothread.h" +#include "common.h" #ifdef _POSIX_THREAD_THREADS_MAX #if _POSIX_THREAD_THREADS_MAX < 64 diff --git a/src/iothread.h b/src/iothread.h index bdec21956..26b775505 100644 --- a/src/iothread.h +++ b/src/iothread.h @@ -1,7 +1,6 @@ /** \file iothread.h Handles IO that may hang. */ - #ifndef FISH_IOTHREAD_H #define FISH_IOTHREAD_H diff --git a/src/key_reader.cpp b/src/key_reader.cpp index 1cb3b3de4..bb4a0075b 100644 --- a/src/key_reader.cpp +++ b/src/key_reader.cpp @@ -5,19 +5,15 @@ Type ^C to exit the program. */ -#include "config.h" - #include #include #include #include -#include #include #include #include "common.h" -#include "fallback.h" - +#include "fallback.h" // IWYU pragma: keep #include "input_common.h" int writestr(char *str) diff --git a/src/kill.cpp b/src/kill.cpp index 7b68b16cd..fb83c8aad 100644 --- a/src/kill.cpp +++ b/src/kill.cpp @@ -5,13 +5,12 @@ and paste with a memory of previous cuts. It supports integration with the X clipboard. */ - -#include "config.h" // IWYU pragma: keep - #include #include #include #include +#include +#include #include "fallback.h" // IWYU pragma: keep #include "kill.h" diff --git a/src/kill.h b/src/kill.h index 686122a29..cdcdf6915 100644 --- a/src/kill.h +++ b/src/kill.h @@ -3,7 +3,6 @@ Works like the killring in emacs and readline. The killring is cut and paste whith a memory of previous cuts. */ - #ifndef FISH_KILL_H #define FISH_KILL_H diff --git a/src/lru.h b/src/lru.h index a70b165d6..3f4181e9f 100644 --- a/src/lru.h +++ b/src/lru.h @@ -2,7 +2,6 @@ Least-recently-used cache implementation */ - #ifndef FISH_LRU_H #define FISH_LRU_H @@ -11,6 +10,7 @@ #include #include #include + #include "common.h" /** A predicate to compare dereferenced pointers */ diff --git a/src/output.cpp b/src/output.cpp index 033d14098..3fda2e67e 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -1,13 +1,11 @@ /** \file output.c Generic output functions */ - #include "config.h" #include #include #include - #if HAVE_NCURSES_H #include #elif HAVE_NCURSES_CURSES_H @@ -15,25 +13,25 @@ #else #include #endif - #if HAVE_TERM_H #include #elif HAVE_NCURSES_TERM_H #include #endif - #include #include #include +#include +#include -#include "fallback.h" -#include "wutil.h" // IWYU pragma: keep - needed for wgettext +#include "fallback.h" // IWYU pragma: keep +#include "wutil.h" // IWYU pragma: keep #include "common.h" #include "output.h" +#include "color.h" static int writeb_internal(char c); - /** The function used for output */ diff --git a/src/output.h b/src/output.h index acdd26127..7f5b077d4 100644 --- a/src/output.h +++ b/src/output.h @@ -4,14 +4,15 @@ /** Constants for various character classifications. Each character of a command string can be classified as one of the following types. */ - #ifndef FISH_OUTPUT_H #define FISH_OUTPUT_H -#include #include +#include +#include + #include "common.h" -#include "fallback.h" +#include "fallback.h" // IWYU pragma: keep #include "color.h" /** diff --git a/src/pager.cpp b/src/pager.cpp index a5cf0eccd..1fb7847ed 100644 --- a/src/pager.cpp +++ b/src/pager.cpp @@ -1,14 +1,19 @@ -#include "config.h" // IWYU pragma: keep - +// IWYU pragma: no_include #include #include #include #include #include +#include + #include "util.h" -#include "wutil.h" // IWYU pragma: keep - needed for wgettext +#include "wutil.h" // IWYU pragma: keep #include "pager.h" #include "highlight.h" +#include "common.h" +#include "screen.h" +#include "complete.h" +#include "reader.h" typedef pager_t::comp_t comp_t; typedef std::vector completion_list_t; diff --git a/src/pager.h b/src/pager.h index 5f7da87ac..c03ca906f 100644 --- a/src/pager.h +++ b/src/pager.h @@ -1,13 +1,15 @@ /** \file pager.h Pager support */ - #ifndef FISH_PAGER_H #define FISH_PAGER_H #include #include #include +#include +#include + #include "common.h" #include "complete.h" #include "screen.h" diff --git a/src/parse_constants.h b/src/parse_constants.h index 4668516b9..497215045 100644 --- a/src/parse_constants.h +++ b/src/parse_constants.h @@ -2,9 +2,8 @@ Constants used in the programmatic representation of fish code. */ - -#ifndef fish_parse_constants_h -#define fish_parse_constants_h +#ifndef FISH_PARSE_CONSTANTS_H +#define FISH_PARSE_CONSTANTS_H #include "config.h" diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp index c54942878..202194e79 100644 --- a/src/parse_execution.cpp +++ b/src/parse_execution.cpp @@ -6,8 +6,6 @@ Non-fatal errors are printed as soon as they are encountered; otherwise you would have to wait for the execution to finish to see them. */ - -#include "parse_execution.h" #include #include #include @@ -18,8 +16,11 @@ #include #include #include -#include // IWYU pragma: keep - suggests instead +#include #include +#include + +#include "parse_execution.h" #include "env.h" #include "event.h" #include "tokenizer.h" @@ -35,6 +36,11 @@ #include "function.h" #include "builtin.h" #include "exec.h" +#include "common.h" +#include "io.h" +#include "parse_constants.h" +#include "parse_tree.h" +#include "proc.h" /* These are the specific statement types that support redirections */ static bool specific_statement_type_is_redirectable_block(const parse_node_t &node) diff --git a/src/parse_execution.h b/src/parse_execution.h index ef796676c..088729cb8 100644 --- a/src/parse_execution.h +++ b/src/parse_execution.h @@ -2,16 +2,17 @@ Provides the "linkage" between a parse_node_tree_t and actual execution structures (job_t, etc.). */ - #ifndef FISH_PARSE_EXECUTION_H #define FISH_PARSE_EXECUTION_H #include + #include "common.h" #include "io.h" #include "parse_constants.h" #include "parse_tree.h" #include "proc.h" +#include class parser_t; struct block_t; diff --git a/src/parse_productions.cpp b/src/parse_productions.cpp index 6ec8e56a2..43a580d30 100644 --- a/src/parse_productions.cpp +++ b/src/parse_productions.cpp @@ -1,7 +1,9 @@ -#include "parse_productions.h" -#include #include + #include "parse_tree.h" +#include "parse_productions.h" +#include "parse_constants.h" +#include "common.h" using namespace parse_productions; diff --git a/src/parse_productions.h b/src/parse_productions.h index ce4565441..e2c2dcc30 100644 --- a/src/parse_productions.h +++ b/src/parse_productions.h @@ -2,13 +2,13 @@ Programmatic representation of fish code. */ - #ifndef FISH_PARSE_TREE_CONSTRUCTION_H #define FISH_PARSE_TREE_CONSTRUCTION_H -#include // for uint8_t, uint32_t -#include "common.h" // for wcstring -#include "parse_constants.h" // for parse_token_type_t, etc +#include +#include + +#include "parse_constants.h" struct parse_token_t; diff --git a/src/parse_tree.cpp b/src/parse_tree.cpp index 95493955d..0c2334ec5 100644 --- a/src/parse_tree.cpp +++ b/src/parse_tree.cpp @@ -1,20 +1,21 @@ #include #include #include -#include #include #include #include +#include +#include +#include + #include "common.h" #include "parse_constants.h" #include "parse_productions.h" #include "parse_tree.h" #include "tokenizer.h" -#include "fallback.h" -#include "wutil.h" // IWYU pragma: keep - needed for wgettext +#include "fallback.h" // IWYU pragma: keep +#include "wutil.h" // IWYU pragma: keep #include "proc.h" -#include -#include // This array provides strings for each symbol in enum parse_token_type_t in parse_constants.h. const wchar_t * const token_type_map[] = { diff --git a/src/parse_tree.h b/src/parse_tree.h index 8ae9ee88d..9fd176450 100644 --- a/src/parse_tree.h +++ b/src/parse_tree.h @@ -2,18 +2,19 @@ Programmatic representation of fish code. */ - #ifndef FISH_PARSE_PRODUCTIONS_H #define FISH_PARSE_PRODUCTIONS_H #include #include -#include +#include +#include +#include +#include #include "common.h" #include "tokenizer.h" #include "parse_constants.h" -#include class parse_node_tree_t; diff --git a/src/parse_util.cpp b/src/parse_util.cpp index 73db28339..5d6721ec4 100644 --- a/src/parse_util.cpp +++ b/src/parse_util.cpp @@ -7,26 +7,26 @@ used in many places in fish and that are somehow related to parsing the code. */ - -#include "config.h" // IWYU pragma: keep - #include #include #include #include #include +#include +#include +#include -#include "fallback.h" +#include "fallback.h" // IWYU pragma: keep #include "util.h" -#include "wutil.h" // IWYU pragma: keep +#include "wutil.h" // IWYU pragma: keep #include "common.h" #include "tokenizer.h" #include "parse_util.h" #include "expand.h" -#include "env.h" #include "wildcard.h" #include "parse_tree.h" #include "builtin.h" +#include "parse_constants.h" /** Error message for improper use of the exec builtin */ #define EXEC_ERR_MSG _(L"The '%ls' command can not be used in a pipeline") diff --git a/src/parse_util.h b/src/parse_util.h index 26f472689..454ac8061 100644 --- a/src/parse_util.h +++ b/src/parse_util.h @@ -3,14 +3,16 @@ Various mostly unrelated utility functions related to parsing, loading and evaluating fish code. */ - #ifndef FISH_PARSE_UTIL_H #define FISH_PARSE_UTIL_H #include #include +#include + #include "common.h" #include "parse_constants.h" +#include "tokenizer.h" /** Find the beginning and end of the first subshell in the specified string. diff --git a/src/parser.cpp b/src/parser.cpp index 2862df609..9202d7fd0 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1,20 +1,17 @@ /** \file parser.c The fish parser. Contains functions for parsing and evaluating code. - */ - -#include "config.h" // IWYU pragma: keep - #include #include #include -#include #include +#include +#include -#include "fallback.h" +#include "fallback.h" // IWYU pragma: keep #include "common.h" -#include "wutil.h" +#include "wutil.h" // IWYU pragma: keep #include "proc.h" #include "parser.h" #include "function.h" @@ -24,10 +21,12 @@ The fish parser. Contains functions for parsing and evaluating code. #include "sanity.h" #include "event.h" #include "intern.h" -#include "signal.h" // IWYU pragma: keep - needed for CHECK_BLOCK #include "parse_util.h" #include "parse_tree.h" #include "parse_execution.h" +#include "parse_constants.h" + +class io_chain_t; /** Error for evaluating in illegal scope diff --git a/src/parser.h b/src/parser.h index af470d36a..d40ca16be 100644 --- a/src/parser.h +++ b/src/parser.h @@ -1,22 +1,22 @@ /** \file parser.h The fish parser. */ - #ifndef FISH_PARSER_H #define FISH_PARSER_H -#include // for size_t -#include // for _List_const_iterator, list, etc +#include +#include +#include +#include #include "common.h" #include "proc.h" #include "event.h" #include "parse_tree.h" -#include "io.h" #include "parse_constants.h" #include "expand.h" -#include +class io_chain_t; /** event_blockage_t represents a block on events of the specified type diff --git a/src/parser_keywords.cpp b/src/parser_keywords.cpp index adf9c40cf..5f9710ac1 100644 --- a/src/parser_keywords.cpp +++ b/src/parser_keywords.cpp @@ -2,9 +2,6 @@ Functions having to do with parser keywords, like testing if a function is a block command. */ - -#include "config.h" // IWYU pragma: keep - #include "fallback.h" // IWYU pragma: keep #include "common.h" #include "parser_keywords.h" diff --git a/src/parser_keywords.h b/src/parser_keywords.h index 94cd0eb75..d9637323a 100644 --- a/src/parser_keywords.h +++ b/src/parser_keywords.h @@ -2,10 +2,11 @@ Functions having to do with parser keywords, like testing if a function is a block command. */ - #ifndef FISH_PARSER_KEYWORD_H #define FISH_PARSER_KEYWORD_H +#include + #include "common.h" /** diff --git a/src/path.cpp b/src/path.cpp index a85b662f3..ebe14165a 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -1,5 +1,3 @@ -#include "config.h" // IWYU pragma: keep - #include #include #include @@ -11,7 +9,7 @@ #include "fallback.h" // IWYU pragma: keep #include "common.h" #include "env.h" -#include "wutil.h" +#include "wutil.h" // IWYU pragma: keep #include "path.h" #include "expand.h" diff --git a/src/path.h b/src/path.h index eec776a6e..ae958b689 100644 --- a/src/path.h +++ b/src/path.h @@ -5,11 +5,12 @@ name can be found in the PATH, and various other path-related issues. */ - #ifndef FISH_PATH_H #define FISH_PATH_H #include +#include + #include "common.h" #include "env.h" diff --git a/src/postfork.cpp b/src/postfork.cpp index 40a0bffb1..f2851eb05 100644 --- a/src/postfork.cpp +++ b/src/postfork.cpp @@ -9,14 +9,19 @@ #include #include #include -#include // IWYU pragma: keep - suggests instead +#include +#if FISH_USE_POSIX_SPAWN +#include +#endif + #include "common.h" #include "proc.h" -#include "wutil.h" +#include "wutil.h" // IWYU pragma: keep #include "signal.h" #include "postfork.h" #include "iothread.h" #include "exec.h" +#include "io.h" #ifndef JOIN_THREADS_BEFORE_FORK #define JOIN_THREADS_BEFORE_FORK 0 diff --git a/src/postfork.h b/src/postfork.h index c277da529..b2ef18ca0 100644 --- a/src/postfork.h +++ b/src/postfork.h @@ -2,24 +2,23 @@ Functions that we may safely call after fork(), of which there are very few. In particular we cannot allocate memory, since we're insane enough to call fork from a multithreaded process. */ - #ifndef FISH_POSTFORK_H #define FISH_POSTFORK_H -#include -#include - #include "config.h" -#include "io.h" +#include #if HAVE_SPAWN_H #include #endif - #ifndef FISH_USE_POSIX_SPAWN #define FISH_USE_POSIX_SPAWN HAVE_SPAWN_H #endif +#include +class io_chain_t; +class job_t; +class process_t; /** This function should be called by both the parent process and the @@ -33,8 +32,6 @@ Returns 0 on sucess, -1 on failiure. */ -class job_t; -class process_t; int set_child_group(job_t *j, process_t *p, int print_errors); /** diff --git a/src/print_help.cpp b/src/print_help.cpp index da401134e..856b87d56 100644 --- a/src/print_help.cpp +++ b/src/print_help.cpp @@ -1,12 +1,9 @@ - /** \file print_help.c Print help message for the specified command */ - #include #include #include -#include #include #include "print_help.h" diff --git a/src/proc.cpp b/src/proc.cpp index 1a0d64792..f4e47cc91 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -7,45 +7,40 @@ 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. - */ +// IWYU pragma: no_include <__bit_reference> #include "config.h" -#include #include #include #include -#include #include #include #include #include -#include -#include // IWYU pragma: keep - suggests instead +#include #include - #include #include -#include - #if HAVE_TERM_H #include #elif HAVE_NCURSES_TERM_H #include #endif - #ifdef HAVE_SIGINFO_H #include #endif - #ifdef HAVE_SYS_SELECT_H #include #endif +#include +#include +#include // IWYU pragma: keep +#include // IWYU pragma: keep #include "fallback.h" // IWYU pragma: keep #include "util.h" - -#include "wutil.h" +#include "wutil.h" // IWYU pragma: keep #include "proc.h" #include "common.h" #include "reader.h" @@ -53,8 +48,9 @@ Some of the code in this file is based on code from the Glibc manual. #include "parser.h" #include "signal.h" #include "event.h" - #include "output.h" +#include "io.h" +#include "parse_tree.h" /** Size of buffer for reading buffered output diff --git a/src/proc.h b/src/proc.h index 3b77fe51e..7fcbe6dad 100644 --- a/src/proc.h +++ b/src/proc.h @@ -7,18 +7,19 @@ needed. */ - #ifndef FISH_PROC_H #define FISH_PROC_H +#include "config.h" // IWYU pragma: keep #include -#include #include -#include // for assert -#include // for size_t -#include // for pid_t, termios +#include +#include +#include +#include +#include +#include // IWYU pragma: keep -#include "config.h" // for HAVE__PROC_SELF_STAT #include "io.h" #include "common.h" #include "parse_tree.h" diff --git a/src/reader.cpp b/src/reader.cpp index 50c8c93d4..3254b905d 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -18,13 +18,12 @@ the end of the list is reached, at which point regular searching will commence. */ - #include "config.h" -#include +// IWYU pragma: no_include +#include #include #include -#include #include #include #include @@ -34,25 +33,22 @@ commence. #include #include #include - #ifdef HAVE_SIGINFO_H #include #endif - #ifdef HAVE_SYS_SELECT_H #include #endif - #include #include #include #include +#include +#include - -#include "fallback.h" +#include "fallback.h" // IWYU pragma: keep #include "util.h" - -#include "wutil.h" +#include "wutil.h" // IWYU pragma: keep #include "highlight.h" #include "reader.h" #include "proc.h" @@ -79,6 +75,8 @@ commence. #include "pager.h" #include "color.h" #include "event.h" +#include "io.h" +#include "parse_constants.h" /** Maximum length of prefix string when printing completion diff --git a/src/reader.h b/src/reader.h index 3d9208a0a..0aa105164 100644 --- a/src/reader.h +++ b/src/reader.h @@ -12,14 +12,16 @@ #include #include #include +#include -#include "io.h" #include "common.h" #include "complete.h" #include "highlight.h" #include "parse_constants.h" class history_t; +class env_vars_snapshot_t; +class io_chain_t; /* Helper class for storing a command line */ class editable_line_t @@ -229,7 +231,6 @@ 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); /** diff --git a/src/sanity.cpp b/src/sanity.cpp index e918c805c..434801266 100644 --- a/src/sanity.cpp +++ b/src/sanity.cpp @@ -1,8 +1,6 @@ /** \file sanity.c Functions for performing sanity checks on the program state */ -#include "config.h" // IWYU pragma: keep - #include #include "fallback.h" // IWYU pragma: keep @@ -13,7 +11,6 @@ #include "reader.h" #include "kill.h" - /** Status from earlier sanity checks */ diff --git a/src/screen.cpp b/src/screen.cpp index 7bb5cf4bd..a4d6f4b35 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -5,15 +5,13 @@ output to screen efficiently by keeping an internal representation of the current screen contents and trying to find the most efficient way for transforming that to the desired screen content. */ - +// IWYU pragma: no_include #include "config.h" #include #include #include - #include - #if HAVE_NCURSES_H #include #elif HAVE_NCURSES_CURSES_H @@ -21,23 +19,19 @@ efficient way for transforming that to the desired screen content. #else #include #endif - #if HAVE_TERM_H #include #elif HAVE_NCURSES_TERM_H #include #endif - #include #include - #include #include #include #include - -#include "fallback.h" +#include "fallback.h" // IWYU pragma: keep #include "common.h" #include "util.h" #include "output.h" diff --git a/src/screen.h b/src/screen.h index e84d8497b..9db4d460e 100644 --- a/src/screen.h +++ b/src/screen.h @@ -18,6 +18,8 @@ #include #include "common.h" #include "highlight.h" +#include +#include class page_rendering_t; diff --git a/src/signal.cpp b/src/signal.cpp index a0eccd081..996d3314b 100644 --- a/src/signal.cpp +++ b/src/signal.cpp @@ -1,29 +1,24 @@ /** \file signal.c The library for various signal related issues - */ - -#include "config.h" // IWYU pragma: keep - -#include #include #include #include - #ifdef HAVE_SIGINFO_H #include #endif +#include +#include #include "common.h" #include "fallback.h" // IWYU pragma: keep -#include "wutil.h" +#include "wutil.h" // IWYU pragma: keep #include "signal.h" #include "event.h" #include "reader.h" #include "proc.h" - /** Struct describing an entry for the lookup table used to convert between signal names and signal ids, etc. diff --git a/src/signal.h b/src/signal.h index fc3e7e735..4a22520a8 100644 --- a/src/signal.h +++ b/src/signal.h @@ -1,12 +1,12 @@ /** \file signal.h The library for various signal related issues - */ #ifndef FISH_SIGNALH #define FISH_SIGNALH #include +#include /** Get the integer signal value representing the specified signal, or diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 4075c73b2..2eca66276 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -5,19 +5,17 @@ future, the tokenizer should be extended to support marks, tokenizing multiple strings and disposing of unused string segments. */ - -#include "config.h" // IWYU pragma: keep - #include #include #include #include #include #include +#include #include "fallback.h" // IWYU pragma: keep #include "common.h" -#include "wutil.h" // IWYU pragma: keep - needed for wgettext +#include "wutil.h" // IWYU pragma: keep #include "tokenizer.h" /* Wow what a hack */ diff --git a/src/tokenizer.h b/src/tokenizer.h index d6de25598..3c3b62364 100644 --- a/src/tokenizer.h +++ b/src/tokenizer.h @@ -5,11 +5,12 @@ tokenizing multiple strings and disposing of unused string segments. */ - #ifndef FISH_TOKENIZER_H #define FISH_TOKENIZER_H #include +#include + #include "common.h" /** diff --git a/src/utf8.cpp b/src/utf8.cpp index 9bd6edf27..7c876a311 100644 --- a/src/utf8.cpp +++ b/src/utf8.cpp @@ -13,15 +13,12 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ - #include -#include - -#include "utf8.h" - +#include // IWYU pragma: keep #include #include -#include + +#include "utf8.h" #define _NXT 0x80 #define _SEQ2 0xc0 diff --git a/src/utf8.h b/src/utf8.h index 33ed6a5ea..72e1cc8bd 100644 --- a/src/utf8.h +++ b/src/utf8.h @@ -21,7 +21,6 @@ #define _UTF8_H_ #include - #include #define UTF8_IGNORE_ERROR 0x01 diff --git a/src/util.cpp b/src/util.cpp index 14cce1c73..9d084af67 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -3,31 +3,17 @@ Contains datastructures such as automatically growing array lists, priority queues, etc. */ - -#include "config.h" - - -#include #include #include -#include -#include -#include -#include -#include #include -#include #include -#include -#include #include -#include +#include -#include "fallback.h" +#include "fallback.h" // IWYU pragma: keep #include "util.h" - #include "common.h" -#include "wutil.h" +#include "wutil.h" // IWYU pragma: keep int wcsfilecmp(const wchar_t *a, const wchar_t *b) { diff --git a/src/util.h b/src/util.h index 1afde0734..d073ef83c 100644 --- a/src/util.h +++ b/src/util.h @@ -5,10 +5,6 @@ #ifndef FISH_UTIL_H #define FISH_UTIL_H -#include -#include -#include - /** Returns the larger of two ints */ diff --git a/src/wcstringutil.cpp b/src/wcstringutil.cpp index e61e331bf..b752409b7 100644 --- a/src/wcstringutil.cpp +++ b/src/wcstringutil.cpp @@ -2,9 +2,7 @@ Helper functions for working with wcstring */ - -#include "config.h" // IWYU pragma: keep - +#include "common.h" #include "wcstringutil.h" typedef wcstring::size_type size_type; diff --git a/src/wcstringutil.h b/src/wcstringutil.h index 4d19fc0b0..d2feec07a 100644 --- a/src/wcstringutil.h +++ b/src/wcstringutil.h @@ -2,12 +2,12 @@ Helper functions for working with wcstring */ - #ifndef FISH_WCSTRINGUTIL_H #define FISH_WCSTRINGUTIL_H #include #include + #include "common.h" /** diff --git a/src/wgetopt.cpp b/src/wgetopt.cpp index 7951eaea0..024767188 100644 --- a/src/wgetopt.cpp +++ b/src/wgetopt.cpp @@ -53,8 +53,6 @@ #include #include -#include "common.h" - /* This needs to come after some library #include to get __GNU_LIBRARY__ defined. */ #ifdef __GNU_LIBRARY__ @@ -74,11 +72,11 @@ GNU application programs can use a third alternative mode in which they can distinguish the relative order of options and other arguments. */ +#include "common.h" #include "wgetopt.h" -#include "wutil.h" +#include "wutil.h" // IWYU pragma: keep #include "fallback.h" // IWYU pragma: keep - /** Use translation functions if available */ diff --git a/src/wgetopt.h b/src/wgetopt.h index 794ccc652..d20529bbe 100644 --- a/src/wgetopt.h +++ b/src/wgetopt.h @@ -47,7 +47,7 @@ Cambridge, MA 02139, USA. */ #ifndef FISH_WGETOPT_H #define FISH_WGETOPT_H -#include +#include class wgetopter_t { diff --git a/src/wildcard.cpp b/src/wildcard.cpp index 202b836d4..471453f62 100644 --- a/src/wildcard.cpp +++ b/src/wildcard.cpp @@ -3,32 +3,27 @@ Fish needs it's own globbing implementation to support tab-expansion of globbed parameters. Also provides recursive wildcards using **. - */ - -#include "config.h" // IWYU pragma: keep -#include #include #include #include #include #include -#include #include #include -#include -#include #include #include +#include +#include +#include -#include "fallback.h" -#include "wutil.h" +#include "fallback.h" // IWYU pragma: keep +#include "wutil.h" // IWYU pragma: keep #include "common.h" #include "wildcard.h" #include "complete.h" #include "reader.h" #include "expand.h" -#include /** Description for generic executable diff --git a/src/wildcard.h b/src/wildcard.h index 3e1d2b6f8..05e185f4c 100644 --- a/src/wildcard.h +++ b/src/wildcard.h @@ -5,14 +5,11 @@ paramaters. */ - #ifndef FISH_WILDCARD_H -/** - Header guard -*/ #define FISH_WILDCARD_H #include +#include #include "common.h" #include "expand.h" diff --git a/src/wutil.cpp b/src/wutil.cpp index 0b11989a4..1cee8320a 100644 --- a/src/wutil.cpp +++ b/src/wutil.cpp @@ -20,11 +20,11 @@ #include #include #include - -#include "fallback.h" +#include #include "common.h" -#include "wutil.h" +#include "fallback.h" // IWYU pragma: keep +#include "wutil.h" // IWYU pragma: keep typedef std::string cstring; diff --git a/src/wutil.h b/src/wutil.h index f16e28c06..a4fee9004 100644 --- a/src/wutil.h +++ b/src/wutil.h @@ -9,10 +9,10 @@ #include #include #include -#include #include #include -#include +#include + #include "common.h" /** From c4c7983497e7be798c3bd1c97df38e8d1d1a58b8 Mon Sep 17 00:00:00 2001 From: Michael Steed Date: Sat, 23 Apr 2016 22:09:17 -0600 Subject: [PATCH 136/363] configure: require at least pcre2-10.21 --- configure.ac | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 90065e9ba..87bf46b7c 100644 --- a/configure.ac +++ b/configure.ac @@ -789,6 +789,7 @@ else AC_MSG_RESULT(no) fi +pcre2_min_version=10.21 EXTRA_PCRE2= AC_ARG_WITH( included-pcre2, @@ -817,8 +818,28 @@ if test "x$included_pcre2" != "xyes"; then # and so AC_CHECK_LIB won't work (can't use a variable as library name) # AC_SEARCH_LIBS will use the existing $LIBS flags with no additional library first AC_SEARCH_LIBS([pcre2_compile_$WCHAR_T_BITS], [], - [ working_pcre2=yes - AC_MSG_NOTICE([using system PCRE2 library]) + [ # pcre2 lib found, check for minimum version + pcre2_version=`$PCRE2_CONFIG --version` + AS_VERSION_COMPARE([$pcre2_version], [$pcre2_min_version], + [ # version < minimum + AC_MSG_NOTICE([system PCRE2 library version $pcre2_version, need $pcre2_min_version or later]) + if test "x$included_pcre2" = "xno"; then + # complain about pcre2 version + AC_MSG_ERROR([system PCRE2 library is too old, but --without-included-pcre2 was given.]) + else + # use the internal version; undo changes to LIBS/CXXFLAGS + included_pcre2=yes + LIBS="$XLIBS" + CXXFLAGS="$XCXXFLAGS" + fi + ], + [ # version == minimum + working_pcre2=yes + ], + [ # version > minimum + working_pcre2=yes + ] + ) ], [ # fail case; undo the changes to LIBS/CXXFLAGS working_pcre2=no @@ -828,7 +849,9 @@ if test "x$included_pcre2" != "xyes"; then ) fi - if test "x$working_pcre2" != "xyes"; then + if test "x$working_pcre2" = "xyes"; then + AC_MSG_NOTICE([using system PCRE2 library]) + else # pcre2 size wrong or pcre2-config not found # is it OK to use the included version? if test "x$included_pcre2" = "xno"; then From c2f9d60eb1f04a3aa0b01d785c1e133cac8ecf1d Mon Sep 17 00:00:00 2001 From: Michael Steed Date: Sat, 23 Apr 2016 18:12:42 -0600 Subject: [PATCH 137/363] Update usage of pcre2_substitute() for pcre2-10.21 - Set PCRE2_SUBSTITUTE_OVERFLOW_LENGTH to get the required buffer length from pcre2 instead of guessing - Set PCRE2_SUBSTITUTE_EXTENDED to enable extra goodies in the replacement string --- src/builtin_string.cpp | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/src/builtin_string.cpp b/src/builtin_string.cpp index 9efa6c2f7..51c824ca8 100644 --- a/src/builtin_string.cpp +++ b/src/builtin_string.cpp @@ -33,7 +33,6 @@ class parser_t; -#define MAX_REPLACE_SIZE size_t(1048576) // pcre2_substitute maximum output size in wchar_t #define STRING_ERR_MISSING _(L"%ls: Expected argument\n") /* externs from builtin.cpp */ @@ -813,7 +812,6 @@ public: bool replace_matches(const wchar_t *arg) { - // A return value of true means all is well (even if no replacements // were performed), false indicates an unrecoverable error. if (regex.code == 0) @@ -822,17 +820,18 @@ public: return false; } - uint32_t options = opts.all ? PCRE2_SUBSTITUTE_GLOBAL : 0; + uint32_t options = PCRE2_SUBSTITUTE_OVERFLOW_LENGTH | PCRE2_SUBSTITUTE_EXTENDED | + (opts.all ? PCRE2_SUBSTITUTE_GLOBAL : 0); size_t arglen = wcslen(arg); PCRE2_SIZE bufsize = (arglen == 0) ? 16 : 2 * arglen; wchar_t *output = (wchar_t *)malloc(sizeof(wchar_t) * bufsize); - if (output == NULL) - { - DIE_MEM(); - } int pcre2_rc = 0; for (;;) { + if (output == NULL) + { + DIE_MEM(); + } PCRE2_SIZE outlen = bufsize; pcre2_rc = pcre2_substitute( regex.code, @@ -847,22 +846,12 @@ public: (PCRE2_UCHAR *)output, &outlen); - if (pcre2_rc == PCRE2_ERROR_NOMEMORY) + if (pcre2_rc == PCRE2_ERROR_NOMEMORY && bufsize < outlen) { - if (bufsize < MAX_REPLACE_SIZE) - { - bufsize = std::min(2 * bufsize, MAX_REPLACE_SIZE); - // cppcheck-suppress memleakOnRealloc - output = (wchar_t *)realloc(output, sizeof(wchar_t) * bufsize); - if (output == NULL) - { - DIE_MEM(); - } - continue; - } - string_error(streams, _(L"%ls: Replacement string too large\n"), argv0); - free(output); - return false; + bufsize = outlen; + // cppcheck-suppress memleakOnRealloc + output = (wchar_t *)realloc(output, sizeof(wchar_t) * bufsize); + continue; } break; } From df10b53c0caecd43a02b7f24515a9ff9edea7056 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Tue, 19 Apr 2016 19:49:15 -0700 Subject: [PATCH 138/363] restyle builtin modules to match project style Now that the IWYU cleanup has been merged compile all, not just a couple, of the builtin modules independent of builtin.cpp. That is, no longer `#include builtin_NAME.cpp` in builtin.cpp. This is more consistent, more in line with what developers expect, and is likely to reduce mistakes. Reduces lint errors from 384 to 336 (-13%). Line count from 6307 to 4988 (-21%). Another step in resolving issue #2902. --- Makefile.in | 253 +++++---- build_tools/iwyu.linux.imp | 2 + build_tools/style.fish | 2 +- src/builtin.cpp | 42 +- src/builtin_commandline.cpp | 605 ++++++++------------ src/builtin_commandline.h | 11 + src/builtin_complete.cpp | 551 +++++++----------- src/builtin_complete.h | 11 + src/builtin_jobs.cpp | 316 ++++------- src/builtin_jobs.h | 11 + src/builtin_printf.cpp | 726 +++++++++++------------- src/builtin_printf.h | 11 + src/builtin_set.cpp | 691 +++++++++-------------- src/builtin_set.h | 11 + src/builtin_set_color.cpp | 201 +++---- src/builtin_set_color.h | 11 + src/builtin_string.cpp | 1050 +++++++++++++---------------------- src/builtin_string.h | 11 + src/builtin_test.cpp | 945 ++++++++++++++----------------- src/builtin_test.h | 9 + src/builtin_ulimit.cpp | 505 ++++++----------- src/builtin_ulimit.h | 11 + 22 files changed, 2397 insertions(+), 3589 deletions(-) create mode 100644 src/builtin_commandline.h create mode 100644 src/builtin_complete.h create mode 100644 src/builtin_jobs.h create mode 100644 src/builtin_printf.h create mode 100644 src/builtin_set.h create mode 100644 src/builtin_set_color.h create mode 100644 src/builtin_string.h create mode 100644 src/builtin_test.h create mode 100644 src/builtin_ulimit.h diff --git a/Makefile.in b/Makefile.in index b73e75d1d..6af1d21cc 100644 --- a/Makefile.in +++ b/Makefile.in @@ -95,29 +95,19 @@ HAVE_DOXYGEN=@HAVE_DOXYGEN@ # All objects that the system needs to build fish, except fish.o # -FISH_OBJS := obj/function.o obj/builtin.o obj/complete.o obj/env.o obj/exec.o \ +FISH_OBJS := obj/function.o obj/builtin.o obj/builtin_commandline.o obj/builtin_complete.o obj/builtin_jobs.o obj/builtin_printf.o obj/builtin_set.o obj/builtin_set_color.o obj/builtin_string.o obj/builtin_test.o obj/builtin_ulimit.o obj/complete.o obj/env.o obj/exec.o \ obj/expand.o obj/highlight.o obj/history.o obj/kill.o obj/parser.o \ obj/proc.o obj/reader.o obj/sanity.o obj/tokenizer.o obj/wildcard.o \ obj/wgetopt.o obj/wutil.o obj/input.o obj/output.o obj/intern.o \ obj/env_universal_common.o obj/input_common.o obj/event.o obj/signal.o \ obj/io.o obj/parse_util.o obj/common.o obj/screen.o obj/path.o \ obj/autoload.o obj/parser_keywords.o obj/iothread.o obj/color.o \ - obj/postfork.o obj/builtin_string.o obj/builtin_test.o obj/parse_tree.o \ + obj/postfork.o obj/parse_tree.o \ obj/parse_productions.o obj/parse_execution.o obj/pager.o obj/utf8.o \ obj/fish_version.o obj/wcstringutil.o FISH_INDENT_OBJS := obj/fish_indent.o obj/print_help.o $(FISH_OBJS) -# -# Additional files used by builtin.o -# - -BUILTIN_FILES := src/builtin_set.cpp src/builtin_commandline.cpp \ - src/builtin_ulimit.cpp src/builtin_complete.cpp \ - src/builtin_jobs.cpp src/builtin_set_color.cpp \ - src/builtin_printf.cpp - - # # All objects that the system needs to build fish_tests # @@ -504,8 +494,6 @@ messages.pot: src/*.cpp src/*.h share/completions/*.fish share/functions/*.fish xgettext -k_ -kN_ src/*.cpp src/*.h -o messages.pot xgettext -j -k_ -kN_ -k--description -LShell --from-code=UTF-8 share/completions/*.fish share/functions/*.fish -o messages.pot -builtin.o: $(BUILTIN_FILES) - ifdef EXTRA_PCRE2 src/builtin_string.cpp: $(PCRE2_H) endif @@ -917,117 +905,151 @@ clean: # DO NOT DELETE THIS LINE -- make depend depends on it. -obj/autoload.o: config.h src/autoload.h src/common.h src/fallback.h -obj/autoload.o: src/signal.h src/lru.h src/wutil.h src/env.h src/exec.h -obj/builtin.o: config.h src/signal.h src/fallback.h src/wutil.h src/common.h -obj/builtin.o: src/builtin.h src/io.h src/function.h src/event.h src/env.h -obj/builtin.o: src/complete.h src/proc.h src/parse_tree.h src/tokenizer.h -obj/builtin.o: src/parse_constants.h src/parser.h src/reader.h -obj/builtin.o: src/highlight.h src/color.h src/wgetopt.h src/input.h -obj/builtin.o: src/input_common.h src/intern.h src/exec.h src/parse_util.h -obj/builtin.o: src/parser_keywords.h src/expand.h src/path.h src/history.h -obj/builtin.o: src/wcstringutil.h src/builtin_set.cpp src/util.h -obj/builtin.o: src/builtin_commandline.cpp src/builtin_complete.cpp -obj/builtin.o: src/builtin_ulimit.cpp src/builtin_jobs.cpp -obj/builtin.o: src/builtin_set_color.cpp src/output.h src/builtin_printf.cpp -obj/builtin.o: src/builtin_string.cpp -obj/builtin_test.o: config.h src/common.h src/fallback.h src/signal.h -obj/builtin_test.o: src/builtin.h src/io.h src/wutil.h src/proc.h -obj/builtin_test.o: src/parse_tree.h src/tokenizer.h src/parse_constants.h +obj/autoload.o: src/autoload.h src/common.h config.h src/fallback.h +obj/autoload.o: src/signal.h src/lru.h src/env.h src/exec.h src/wutil.h +obj/builtin.o: src/builtin.h src/common.h config.h src/fallback.h +obj/builtin.o: src/signal.h src/builtin_commandline.h src/builtin_complete.h +obj/builtin.o: src/builtin_jobs.h src/builtin_printf.h src/builtin_set.h +obj/builtin.o: src/builtin_set_color.h src/builtin_string.h +obj/builtin.o: src/builtin_test.h src/builtin_ulimit.h src/complete.h +obj/builtin.o: src/env.h src/event.h src/exec.h src/expand.h +obj/builtin.o: src/parse_constants.h src/function.h src/highlight.h +obj/builtin.o: src/color.h src/history.h src/wutil.h src/input.h src/intern.h +obj/builtin.o: src/io.h src/parse_util.h src/tokenizer.h src/parser.h +obj/builtin.o: src/proc.h src/parse_tree.h src/parser_keywords.h src/path.h +obj/builtin.o: src/reader.h src/wcstringutil.h src/wgetopt.h +obj/builtin_commandline.o: src/builtin.h src/common.h config.h src/fallback.h +obj/builtin_commandline.o: src/signal.h src/input.h src/env.h src/io.h +obj/builtin_commandline.o: src/parse_util.h src/parse_constants.h +obj/builtin_commandline.o: src/tokenizer.h src/proc.h src/parse_tree.h +obj/builtin_commandline.o: src/reader.h src/complete.h src/highlight.h +obj/builtin_commandline.o: src/color.h src/util.h src/wgetopt.h src/wutil.h +obj/builtin_complete.o: src/builtin.h src/common.h config.h src/fallback.h +obj/builtin_complete.o: src/signal.h src/complete.h src/env.h src/io.h +obj/builtin_complete.o: src/parse_constants.h src/parse_util.h +obj/builtin_complete.o: src/tokenizer.h src/parser.h src/proc.h +obj/builtin_complete.o: src/parse_tree.h src/event.h src/expand.h +obj/builtin_complete.o: src/reader.h src/highlight.h src/color.h +obj/builtin_complete.o: src/wgetopt.h src/wutil.h +obj/builtin_jobs.o: config.h src/builtin.h src/common.h src/fallback.h +obj/builtin_jobs.o: src/signal.h src/io.h src/proc.h src/parse_tree.h +obj/builtin_jobs.o: src/tokenizer.h src/parse_constants.h src/wgetopt.h +obj/builtin_jobs.o: src/wutil.h +obj/builtin_printf.o: src/builtin.h src/common.h config.h src/fallback.h +obj/builtin_printf.o: src/signal.h src/io.h src/proc.h src/parse_tree.h +obj/builtin_printf.o: src/tokenizer.h src/parse_constants.h src/wutil.h +obj/builtin_set.o: src/builtin.h src/common.h config.h src/fallback.h +obj/builtin_set.o: src/signal.h src/env.h src/expand.h src/parse_constants.h +obj/builtin_set.o: src/io.h src/proc.h src/parse_tree.h src/tokenizer.h +obj/builtin_set.o: src/wgetopt.h src/wutil.h +obj/builtin_set_color.o: config.h src/builtin.h src/common.h src/fallback.h +obj/builtin_set_color.o: src/signal.h src/color.h src/io.h src/output.h +obj/builtin_set_color.o: src/proc.h src/parse_tree.h src/tokenizer.h +obj/builtin_set_color.o: src/parse_constants.h src/wgetopt.h src/wutil.h +obj/builtin_string.o: config.h src/builtin.h src/common.h src/fallback.h +obj/builtin_string.o: src/signal.h src/io.h src/parse_util.h +obj/builtin_string.o: src/parse_constants.h src/tokenizer.h src/wgetopt.h +obj/builtin_string.o: src/wildcard.h src/expand.h src/complete.h src/wutil.h +obj/builtin_test.o: src/builtin.h src/common.h config.h src/fallback.h +obj/builtin_test.o: src/signal.h src/io.h src/proc.h src/parse_tree.h +obj/builtin_test.o: src/tokenizer.h src/parse_constants.h src/wutil.h +obj/builtin_ulimit.o: src/builtin.h src/common.h config.h src/fallback.h +obj/builtin_ulimit.o: src/signal.h src/io.h src/util.h src/wgetopt.h +obj/builtin_ulimit.o: src/wutil.h obj/color.o: src/color.h src/common.h config.h src/fallback.h src/signal.h obj/common.o: config.h src/signal.h src/fallback.h src/wutil.h src/common.h obj/common.o: src/expand.h src/parse_constants.h src/wildcard.h obj/common.o: src/complete.h src/util.cpp src/util.h src/fallback.cpp -obj/complete.o: config.h src/fallback.h src/signal.h src/util.h +obj/complete.o: src/fallback.h config.h src/signal.h src/util.h obj/complete.o: src/wildcard.h src/common.h src/expand.h obj/complete.o: src/parse_constants.h src/complete.h src/proc.h src/io.h obj/complete.o: src/parse_tree.h src/tokenizer.h src/parser.h src/event.h obj/complete.o: src/function.h src/env.h src/builtin.h src/exec.h obj/complete.o: src/parse_util.h src/wutil.h src/path.h src/iothread.h obj/complete.o: src/autoload.h src/lru.h -obj/env.o: config.h src/fallback.h src/signal.h src/wutil.h src/common.h +obj/env.o: src/fallback.h config.h src/signal.h src/wutil.h src/common.h obj/env.o: src/proc.h src/io.h src/parse_tree.h src/tokenizer.h obj/env.o: src/parse_constants.h src/env.h src/sanity.h src/expand.h obj/env.o: src/history.h src/reader.h src/complete.h src/highlight.h -obj/env.o: src/color.h src/env_universal_common.h src/input.h -obj/env.o: src/input_common.h src/event.h src/path.h src/fish_version.h +obj/env.o: src/color.h src/env_universal_common.h src/input.h src/event.h +obj/env.o: src/path.h src/fish_version.h src/input_common.h obj/env_universal_common.o: config.h src/env_universal_common.h src/common.h obj/env_universal_common.o: src/fallback.h src/signal.h src/wutil.h src/env.h obj/env_universal_common.o: src/util.h src/utf8.h -obj/event.o: config.h src/signal.h src/fallback.h src/wutil.h src/common.h +obj/event.o: src/signal.h src/fallback.h config.h src/wutil.h src/common.h obj/event.o: src/input_common.h src/proc.h src/io.h src/parse_tree.h obj/event.o: src/tokenizer.h src/parse_constants.h src/parser.h src/event.h -obj/exec.o: config.h src/signal.h src/fallback.h src/postfork.h src/io.h -obj/exec.o: src/common.h src/wutil.h src/proc.h src/parse_tree.h -obj/exec.o: src/tokenizer.h src/parse_constants.h src/exec.h src/parser.h -obj/exec.o: src/event.h src/builtin.h src/function.h src/env.h -obj/exec.o: src/parse_util.h +obj/event.o: src/expand.h +obj/exec.o: config.h src/signal.h src/fallback.h src/postfork.h src/common.h +obj/exec.o: src/wutil.h src/proc.h src/io.h src/parse_tree.h src/tokenizer.h +obj/exec.o: src/parse_constants.h src/exec.h src/parser.h src/event.h +obj/exec.o: src/expand.h src/builtin.h src/function.h src/env.h src/reader.h +obj/exec.o: src/complete.h src/highlight.h src/color.h obj/expand.o: config.h src/fallback.h src/signal.h src/util.h src/common.h obj/expand.o: src/wutil.h src/env.h src/proc.h src/io.h src/parse_tree.h -obj/expand.o: src/tokenizer.h src/parse_constants.h src/parser.h src/event.h -obj/expand.o: src/expand.h src/wildcard.h src/complete.h src/exec.h -obj/expand.o: src/iothread.h src/parse_util.h +obj/expand.o: src/tokenizer.h src/parse_constants.h src/path.h src/expand.h +obj/expand.o: src/wildcard.h src/complete.h src/exec.h src/iothread.h +obj/expand.o: src/parse_util.h obj/fish.o: config.h src/fallback.h src/signal.h src/common.h src/reader.h -obj/fish.o: src/io.h src/complete.h src/highlight.h src/env.h src/color.h +obj/fish.o: src/complete.h src/highlight.h src/env.h src/color.h obj/fish.o: src/parse_constants.h src/builtin.h src/function.h src/event.h -obj/fish.o: src/wutil.h src/proc.h src/parse_tree.h src/tokenizer.h -obj/fish.o: src/parser.h src/expand.h src/intern.h src/history.h src/path.h -obj/fish.o: src/input.h src/input_common.h src/fish_version.h -obj/fish_indent.o: config.h src/color.h src/common.h src/fallback.h +obj/fish.o: src/wutil.h src/proc.h src/io.h src/parse_tree.h src/tokenizer.h +obj/fish.o: src/parser.h src/expand.h src/history.h src/path.h src/input.h +obj/fish.o: src/fish_version.h src/input_common.h src/wildcard.h +obj/fish_indent.o: src/color.h src/common.h config.h src/fallback.h obj/fish_indent.o: src/signal.h src/highlight.h src/env.h obj/fish_indent.o: src/parse_constants.h src/wutil.h src/output.h src/input.h -obj/fish_indent.o: src/input_common.h src/parse_tree.h src/tokenizer.h -obj/fish_indent.o: src/print_help.h src/fish_version.h -obj/fish_tests.o: config.h src/signal.h src/fallback.h src/util.h +obj/fish_indent.o: src/parse_tree.h src/tokenizer.h src/print_help.h +obj/fish_indent.o: src/fish_version.h +obj/fish_tests.o: src/signal.h src/fallback.h config.h src/util.h obj/fish_tests.o: src/common.h src/proc.h src/io.h src/parse_tree.h obj/fish_tests.o: src/tokenizer.h src/parse_constants.h src/reader.h obj/fish_tests.o: src/complete.h src/highlight.h src/env.h src/color.h -obj/fish_tests.o: src/builtin.h src/function.h src/event.h src/autoload.h -obj/fish_tests.o: src/lru.h src/wutil.h src/expand.h src/parser.h -obj/fish_tests.o: src/output.h src/exec.h src/path.h src/history.h -obj/fish_tests.o: src/iothread.h src/postfork.h src/parse_util.h src/pager.h -obj/fish_tests.o: src/screen.h src/input.h src/input_common.h src/wildcard.h -obj/fish_tests.o: src/utf8.h src/env_universal_common.h src/wcstringutil.h +obj/fish_tests.o: src/builtin.h src/function.h src/event.h src/wutil.h +obj/fish_tests.o: src/expand.h src/parser.h src/path.h src/history.h +obj/fish_tests.o: src/iothread.h src/parse_util.h src/pager.h src/screen.h +obj/fish_tests.o: src/input.h src/wildcard.h src/utf8.h +obj/fish_tests.o: src/env_universal_common.h src/wcstringutil.h src/lru.h +obj/fish_tests.o: src/input_common.h obj/fish_version.o: src/fish_version.h -obj/function.o: config.h src/wutil.h src/common.h src/fallback.h src/signal.h +obj/function.o: src/wutil.h src/common.h config.h src/fallback.h src/signal.h obj/function.o: src/autoload.h src/lru.h src/function.h src/event.h src/env.h -obj/function.o: src/intern.h src/reader.h src/io.h src/complete.h -obj/function.o: src/highlight.h src/color.h src/parse_constants.h -obj/function.o: src/parser_keywords.h -obj/highlight.o: config.h src/fallback.h src/signal.h src/wutil.h +obj/function.o: src/intern.h src/reader.h src/complete.h src/highlight.h +obj/function.o: src/color.h src/parse_constants.h src/parser_keywords.h +obj/highlight.o: src/fallback.h config.h src/signal.h src/wutil.h obj/highlight.o: src/common.h src/highlight.h src/env.h src/color.h obj/highlight.o: src/tokenizer.h src/parse_util.h src/parse_constants.h -obj/highlight.o: src/builtin.h src/io.h src/function.h src/event.h -obj/highlight.o: src/expand.h src/output.h src/wildcard.h src/complete.h -obj/highlight.o: src/path.h src/history.h src/parse_tree.h -obj/history.o: config.h src/fallback.h src/signal.h src/sanity.h src/reader.h -obj/history.o: src/io.h src/common.h src/complete.h src/highlight.h src/env.h -obj/history.o: src/color.h src/parse_constants.h src/parse_tree.h -obj/history.o: src/tokenizer.h src/wutil.h src/history.h src/path.h -obj/history.o: src/iothread.h src/lru.h +obj/highlight.o: src/builtin.h src/function.h src/event.h src/expand.h +obj/highlight.o: src/output.h src/wildcard.h src/complete.h src/path.h +obj/highlight.o: src/history.h src/parse_tree.h +obj/history.o: src/common.h config.h src/fallback.h src/signal.h src/env.h +obj/history.o: src/history.h src/wutil.h src/iothread.h src/lru.h +obj/history.o: src/parse_constants.h src/parse_tree.h src/tokenizer.h +obj/history.o: src/path.h src/reader.h src/complete.h src/highlight.h +obj/history.o: src/color.h src/sanity.h +obj/input.o: config.h src/fallback.h src/signal.h src/wutil.h src/common.h +obj/input.o: src/reader.h src/complete.h src/highlight.h src/env.h +obj/input.o: src/color.h src/parse_constants.h src/proc.h src/io.h +obj/input.o: src/parse_tree.h src/tokenizer.h src/input_common.h src/input.h +obj/input.o: src/parser.h src/event.h src/expand.h src/output.h obj/input_common.o: config.h src/fallback.h src/signal.h src/util.h obj/input_common.o: src/common.h src/input_common.h obj/input_common.o: src/env_universal_common.h src/wutil.h src/env.h obj/input_common.o: src/iothread.h -obj/input.o: config.h src/fallback.h src/signal.h src/wutil.h src/common.h -obj/input.o: src/reader.h src/io.h src/complete.h src/highlight.h src/env.h -obj/input.o: src/color.h src/parse_constants.h src/proc.h src/parse_tree.h -obj/input.o: src/tokenizer.h src/input_common.h src/input.h src/parser.h -obj/input.o: src/event.h src/output.h -obj/intern.o: config.h src/fallback.h src/signal.h src/common.h src/intern.h -obj/io.o: config.h src/fallback.h src/signal.h src/wutil.h src/common.h +obj/intern.o: src/fallback.h config.h src/signal.h src/common.h src/intern.h +obj/io.o: src/fallback.h config.h src/signal.h src/wutil.h src/common.h obj/io.o: src/exec.h src/io.h -obj/iothread.o: config.h src/iothread.h src/common.h src/fallback.h -obj/iothread.o: src/signal.h -obj/key_reader.o: config.h src/common.h src/fallback.h src/signal.h +obj/iothread.o: src/signal.h src/iothread.h src/common.h config.h +obj/iothread.o: src/fallback.h +obj/key_reader.o: src/common.h config.h src/fallback.h src/signal.h obj/key_reader.o: src/input_common.h -obj/kill.o: config.h src/fallback.h src/signal.h src/kill.h src/common.h +obj/kill.o: src/fallback.h config.h src/signal.h src/kill.h src/common.h obj/kill.o: src/env.h src/exec.h src/path.h obj/output.o: config.h src/fallback.h src/signal.h src/wutil.h src/common.h obj/output.o: src/output.h src/color.h -obj/pager.o: config.h src/util.h src/wutil.h src/common.h src/fallback.h +obj/pager.o: src/util.h src/wutil.h src/common.h config.h src/fallback.h obj/pager.o: src/signal.h src/pager.h src/complete.h src/screen.h -obj/pager.o: src/highlight.h src/env.h src/color.h src/reader.h src/io.h +obj/pager.o: src/highlight.h src/env.h src/color.h src/reader.h obj/pager.o: src/parse_constants.h obj/parse_execution.o: src/parse_execution.h src/common.h config.h obj/parse_execution.o: src/fallback.h src/signal.h src/io.h @@ -1037,26 +1059,26 @@ obj/parse_execution.o: src/parse_util.h src/complete.h src/wildcard.h obj/parse_execution.o: src/expand.h src/parser.h src/reader.h src/highlight.h obj/parse_execution.o: src/color.h src/wutil.h src/path.h src/function.h obj/parse_execution.o: src/builtin.h src/exec.h -obj/parse_productions.o: src/parse_productions.h src/common.h config.h -obj/parse_productions.o: src/fallback.h src/signal.h src/parse_constants.h -obj/parse_productions.o: src/parse_tree.h src/tokenizer.h -obj/parser.o: config.h src/fallback.h src/signal.h src/common.h src/wutil.h -obj/parser.o: src/proc.h src/io.h src/parse_tree.h src/tokenizer.h -obj/parser.o: src/parse_constants.h src/parser.h src/event.h src/function.h -obj/parser.o: src/env.h src/expand.h src/reader.h src/complete.h -obj/parser.o: src/highlight.h src/color.h src/sanity.h src/intern.h -obj/parser.o: src/parse_util.h src/parse_execution.h -obj/parser_keywords.o: config.h src/fallback.h src/signal.h src/common.h -obj/parser_keywords.o: src/parser_keywords.h +obj/parse_productions.o: src/parse_tree.h src/common.h config.h +obj/parse_productions.o: src/fallback.h src/signal.h src/tokenizer.h +obj/parse_productions.o: src/parse_constants.h src/parse_productions.h obj/parse_tree.o: src/common.h config.h src/fallback.h src/signal.h obj/parse_tree.o: src/parse_constants.h src/parse_productions.h obj/parse_tree.o: src/parse_tree.h src/tokenizer.h src/wutil.h src/proc.h obj/parse_tree.o: src/io.h -obj/parse_util.o: config.h src/fallback.h src/signal.h src/util.h src/wutil.h +obj/parse_util.o: src/fallback.h config.h src/signal.h src/util.h src/wutil.h obj/parse_util.o: src/common.h src/tokenizer.h src/parse_util.h -obj/parse_util.o: src/parse_constants.h src/expand.h src/env.h src/wildcard.h -obj/parse_util.o: src/complete.h src/parse_tree.h src/builtin.h src/io.h -obj/path.o: config.h src/fallback.h src/signal.h src/common.h src/env.h +obj/parse_util.o: src/parse_constants.h src/expand.h src/wildcard.h +obj/parse_util.o: src/complete.h src/parse_tree.h src/builtin.h +obj/parser.o: src/fallback.h config.h src/signal.h src/common.h src/wutil.h +obj/parser.o: src/proc.h src/io.h src/parse_tree.h src/tokenizer.h +obj/parser.o: src/parse_constants.h src/parser.h src/event.h src/expand.h +obj/parser.o: src/function.h src/env.h src/reader.h src/complete.h +obj/parser.o: src/highlight.h src/color.h src/sanity.h src/intern.h +obj/parser.o: src/parse_util.h src/parse_execution.h +obj/parser_keywords.o: src/fallback.h config.h src/signal.h src/common.h +obj/parser_keywords.o: src/parser_keywords.h +obj/path.o: src/fallback.h config.h src/signal.h src/common.h src/env.h obj/path.o: src/wutil.h src/path.h src/expand.h src/parse_constants.h obj/postfork.o: src/signal.h src/common.h config.h src/fallback.h src/proc.h obj/postfork.o: src/io.h src/parse_tree.h src/tokenizer.h @@ -1067,36 +1089,35 @@ obj/proc.o: config.h src/signal.h src/fallback.h src/util.h src/wutil.h obj/proc.o: src/common.h src/proc.h src/io.h src/parse_tree.h src/tokenizer.h obj/proc.o: src/parse_constants.h src/reader.h src/complete.h src/highlight.h obj/proc.o: src/env.h src/color.h src/sanity.h src/parser.h src/event.h -obj/proc.o: src/output.h +obj/proc.o: src/expand.h src/output.h obj/reader.o: config.h src/signal.h src/fallback.h src/util.h src/wutil.h obj/reader.o: src/common.h src/highlight.h src/env.h src/color.h src/reader.h -obj/reader.o: src/io.h src/complete.h src/parse_constants.h src/proc.h +obj/reader.o: src/complete.h src/parse_constants.h src/proc.h src/io.h obj/reader.o: src/parse_tree.h src/tokenizer.h src/parser.h src/event.h -obj/reader.o: src/history.h src/sanity.h src/exec.h src/expand.h src/kill.h +obj/reader.o: src/expand.h src/history.h src/sanity.h src/exec.h src/kill.h obj/reader.o: src/input_common.h src/input.h src/function.h src/output.h obj/reader.o: src/screen.h src/iothread.h src/intern.h src/parse_util.h obj/reader.o: src/pager.h -obj/sanity.o: config.h src/fallback.h src/signal.h src/common.h src/sanity.h +obj/sanity.o: src/fallback.h config.h src/signal.h src/common.h src/sanity.h obj/sanity.o: src/proc.h src/io.h src/parse_tree.h src/tokenizer.h obj/sanity.o: src/parse_constants.h src/history.h src/wutil.h src/reader.h obj/sanity.o: src/complete.h src/highlight.h src/env.h src/color.h src/kill.h obj/screen.o: config.h src/fallback.h src/signal.h src/common.h src/util.h obj/screen.o: src/output.h src/color.h src/highlight.h src/env.h src/screen.h -obj/screen.o: src/pager.h src/complete.h src/reader.h src/io.h -obj/screen.o: src/parse_constants.h -obj/signal.o: config.h src/signal.h src/common.h src/fallback.h src/wutil.h -obj/signal.o: src/event.h src/reader.h src/io.h src/complete.h -obj/signal.o: src/highlight.h src/env.h src/color.h src/parse_constants.h -obj/signal.o: src/proc.h src/parse_tree.h src/tokenizer.h -obj/tokenizer.o: config.h src/fallback.h src/signal.h src/common.h +obj/screen.o: src/pager.h src/complete.h src/reader.h src/parse_constants.h +obj/signal.o: src/signal.h src/common.h config.h src/fallback.h src/wutil.h +obj/signal.o: src/event.h src/reader.h src/complete.h src/highlight.h +obj/signal.o: src/env.h src/color.h src/parse_constants.h src/proc.h src/io.h +obj/signal.o: src/parse_tree.h src/tokenizer.h +obj/tokenizer.o: src/fallback.h config.h src/signal.h src/common.h obj/tokenizer.o: src/wutil.h src/tokenizer.h obj/utf8.o: src/utf8.h -obj/wcstringutil.o: config.h src/wcstringutil.h src/common.h src/fallback.h -obj/wcstringutil.o: src/signal.h +obj/wcstringutil.o: src/common.h config.h src/fallback.h src/signal.h +obj/wcstringutil.o: src/wcstringutil.h obj/wgetopt.o: config.h src/common.h src/fallback.h src/signal.h obj/wgetopt.o: src/wgetopt.h src/wutil.h -obj/wildcard.o: config.h src/fallback.h src/signal.h src/wutil.h src/common.h +obj/wildcard.o: src/fallback.h config.h src/signal.h src/wutil.h src/common.h obj/wildcard.o: src/wildcard.h src/expand.h src/parse_constants.h -obj/wildcard.o: src/complete.h src/reader.h src/io.h src/highlight.h -obj/wildcard.o: src/env.h src/color.h -obj/wutil.o: config.h src/fallback.h src/signal.h src/common.h src/wutil.h +obj/wildcard.o: src/complete.h src/reader.h src/highlight.h src/env.h +obj/wildcard.o: src/color.h +obj/wutil.o: config.h src/common.h src/fallback.h src/signal.h src/wutil.h diff --git a/build_tools/iwyu.linux.imp b/build_tools/iwyu.linux.imp index 6fc614aea..27700b676 100644 --- a/build_tools/iwyu.linux.imp +++ b/build_tools/iwyu.linux.imp @@ -14,4 +14,6 @@ { symbol: ["size_t", "private", "", "public"] }, { symbol: ["size_t", "private", "", "public"] }, { symbol: ["uint64_t", "private", "", "public"] }, + { symbol: ["memset", "private", "", "public"] }, + { symbol: ["strerror", "private", "", "public"] }, ] diff --git a/build_tools/style.fish b/build_tools/style.fish index 62d8d5c7e..fc9a96e3a 100755 --- a/build_tools/style.fish +++ b/build_tools/style.fish @@ -39,7 +39,7 @@ else set git_clang_format yes else # No pending changes so lint the files in the most recent commit. - set files (git show --name-only --pretty=oneline head | tail --lines=+2) + set files (git show --name-only --pretty=oneline | tail --lines=+2) end # Extract just the C/C++ files. diff --git a/src/builtin.cpp b/src/builtin.cpp index b7a84c693..b03565436 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -1,3 +1,4 @@ +// // Functions for executing builtin functions. // // How to add a new builtin function: @@ -19,6 +20,8 @@ #include #include #include +#include +#include #include #include #include @@ -29,24 +32,33 @@ #include #include #include +#include // IWYU pragma: keep #include #include -#include -#include // IWYU pragma: keep -#include -#include "fallback.h" // IWYU pragma: keep #include "builtin.h" +#include "builtin_commandline.h" +#include "builtin_complete.h" +#include "builtin_jobs.h" +#include "builtin_printf.h" +#include "builtin_set.h" +#include "builtin_set_color.h" +#include "builtin_string.h" +#include "builtin_test.h" +#include "builtin_ulimit.h" +#include "common.h" #include "complete.h" #include "env.h" #include "event.h" #include "exec.h" #include "expand.h" +#include "fallback.h" // IWYU pragma: keep #include "function.h" #include "highlight.h" #include "history.h" #include "input.h" #include "intern.h" +#include "io.h" #include "parse_constants.h" #include "parse_util.h" #include "parser.h" @@ -58,9 +70,7 @@ #include "tokenizer.h" #include "wcstringutil.h" #include "wgetopt.h" -#include "wutil.h" -#include "common.h" -#include "io.h" +#include "wutil.h" // IWYU pragma: keep // The default prompt for the read command. #define DEFAULT_READ_PROMPT L"set_color green; echo -n read; set_color normal; echo -n \"> \"" @@ -241,7 +251,7 @@ void builtin_missing_argument(parser_t &parser, io_streams_t &streams, const wch builtin_print_help(parser, streams, cmd, streams.err); } -// Here follows the definition of all builtin commands. The function names are all on the form +// Here follows the definition of all builtin commands. The function names are all of the form // builtin_NAME where NAME is the name of the builtin. so the function name for the builtin 'fg' is // 'builtin_fg'. // @@ -253,21 +263,7 @@ void builtin_missing_argument(parser_t &parser, io_streams_t &streams, const wch // implementation, namely 'builtin_break_continue. // // Several other builtins, including jobs, ulimit and set are so big that they have been given their -// own file. These files are all named 'builtin_NAME.c', where NAME is the name of the builtin. -// These files are included directly below. -#include "builtin_commandline.cpp" -#include "builtin_complete.cpp" -#include "builtin_jobs.cpp" -#include "builtin_printf.cpp" -#include "builtin_set.cpp" -#include "builtin_set_color.cpp" -#include "builtin_ulimit.cpp" - -// builtin_test lives in builtin_test.cpp -int builtin_test(parser_t &parser, io_streams_t &streams, wchar_t **argv); - -// builtin_string lives in builtin_string.cpp -int builtin_string(parser_t &parser, io_streams_t &streams, wchar_t **argv); +// own module. These files are all named 'builtin_NAME.cpp', where NAME is the name of the builtin. /// List a single key binding. /// Returns false if no binding with that sequence and mode exists. diff --git a/src/builtin_commandline.cpp b/src/builtin_commandline.cpp index e84bcb073..0ccea6165 100644 --- a/src/builtin_commandline.cpp +++ b/src/builtin_commandline.cpp @@ -1,110 +1,81 @@ -/** \file builtin_commandline.c Functions defining the commandline builtin - -Functions used for implementing the commandline builtin. -*/ +// Functions used for implementing the commandline builtin. +#include +#include +#include +#include #include #include -#include -#include -#include #include -#include -#include "fallback.h" // IWYU pragma: keep -#include "util.h" #include "builtin.h" #include "common.h" -#include "wgetopt.h" -#include "reader.h" -#include "proc.h" -#include "tokenizer.h" +#include "fallback.h" // IWYU pragma: keep #include "input.h" -#include "parse_util.h" #include "io.h" +#include "parse_util.h" +#include "proc.h" +#include "reader.h" +#include "tokenizer.h" +#include "util.h" +#include "wgetopt.h" #include "wutil.h" // IWYU pragma: keep class parser_t; -/** - Which part of the comandbuffer are we operating on -*/ -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 */ -} -; +/// Which part of the comandbuffer are we operating on. +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 +}; -/** - 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 */ -} -; +/// 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 +}; -/** - Pointer to what the commandline builtin considers to be the current - contents of the command line buffer. - */ -static const wchar_t *current_buffer=0; -/** - What the commandline builtin considers to be the current cursor - position. - */ +/// Pointer to what the commandline builtin considers to be the current contents of the command line +/// buffer. +static const wchar_t *current_buffer = 0; +// What the commandline builtin considers to be the current cursor position. static size_t current_cursor_pos = (size_t)(-1); -/** - Returns the current commandline buffer. -*/ -static const wchar_t *get_buffer() -{ - return current_buffer; -} +/// Returns the current commandline buffer. +static const wchar_t *get_buffer() { return current_buffer; } -/** - Returns the position of the cursor -*/ -static size_t get_cursor_pos() -{ - return current_cursor_pos; -} +/// Returns the position of the cursor. +static size_t get_cursor_pos() { return current_cursor_pos; } static pthread_mutex_t transient_commandline_lock = PTHREAD_MUTEX_INITIALIZER; -static wcstring_list_t *get_transient_stack() -{ +static wcstring_list_t *get_transient_stack() { ASSERT_IS_MAIN_THREAD(); ASSERT_IS_LOCKED(transient_commandline_lock); - // A pointer is a little more efficient than an object as a static because we can elide the thread-safe initialization + // A pointer is a little more efficient than an object as a static because we can elide the + // thread-safe initialization. static wcstring_list_t *result = NULL; - if (! result) - { + if (!result) { result = new wcstring_list_t(); } return result; } -static bool get_top_transient(wcstring *out_result) -{ +static bool get_top_transient(wcstring *out_result) { ASSERT_IS_MAIN_THREAD(); bool result = false; scoped_lock locker(transient_commandline_lock); const wcstring_list_t *stack = get_transient_stack(); - if (! stack->empty()) - { + if (!stack->empty()) { out_result->assign(stack->back()); result = true; } return result; } -builtin_commandline_scoped_transient_t::builtin_commandline_scoped_transient_t(const wcstring &cmd) -{ +builtin_commandline_scoped_transient_t::builtin_commandline_scoped_transient_t( + const wcstring &cmd) { ASSERT_IS_MAIN_THREAD(); scoped_lock locker(transient_commandline_lock); wcstring_list_t *stack = get_transient_stack(); @@ -112,8 +83,7 @@ builtin_commandline_scoped_transient_t::builtin_commandline_scoped_transient_t(c this->token = stack->size(); } -builtin_commandline_scoped_transient_t::~builtin_commandline_scoped_transient_t() -{ +builtin_commandline_scoped_transient_t::~builtin_commandline_scoped_transient_t() { ASSERT_IS_MAIN_THREAD(); scoped_lock locker(transient_commandline_lock); wcstring_list_t *stack = get_transient_stack(); @@ -121,19 +91,15 @@ builtin_commandline_scoped_transient_t::~builtin_commandline_scoped_transient_t( stack->pop_back(); } -/** - Replace/append/insert the selection with/at/after the specified string. - - \param begin beginning of selection - \param end end of selection - \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) -{ +/// Replace/append/insert the selection with/at/after the specified string. +/// +/// \param begin beginning of selection +/// \param end end of selection +/// \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) { const wchar_t *buff = get_buffer(); size_t out_pos = get_cursor_pos(); @@ -141,29 +107,23 @@ static void replace_part(const wchar_t *begin, out.append(buff, begin - buff); - switch (append_mode) - { - case REPLACE_MODE: - { - + switch (append_mode) { + case REPLACE_MODE: { out.append(insert); - out_pos = wcslen(insert) + (begin-buff); + out_pos = wcslen(insert) + (begin - buff); break; - } - case APPEND_MODE: - { - out.append(begin, end-begin); + case APPEND_MODE: { + out.append(begin, end - begin); out.append(insert); break; } - case INSERT_MODE: - { - long cursor = get_cursor_pos() -(begin-buff); + 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); + out.append(begin + cursor, end - begin - cursor); + out_pos += wcslen(insert); break; } } @@ -171,84 +131,60 @@ static void replace_part(const wchar_t *begin, reader_set_buffer(out, out_pos); } -/** - Output the specified selection. +/// Output the specified selection. +/// +/// \param begin start of selection +/// \param end end of selection +/// \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, + io_streams_t &streams) { + size_t pos = get_cursor_pos() - (begin - get_buffer()); - \param begin start of selection - \param end end of selection - \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, - io_streams_t &streams) -{ - size_t pos = get_cursor_pos()-(begin-get_buffer()); - - if (tokenize) - { - wchar_t *buff = wcsndup(begin, end-begin); -// fwprintf( stderr, L"Subshell: %ls, end char %lc\n", buff, *end ); + if (tokenize) { + wchar_t *buff = wcsndup(begin, end - begin); + // fwprintf( stderr, L"Subshell: %ls, end char %lc\n", buff, *end ); wcstring out; tokenizer_t tok(buff, TOK_ACCEPT_UNFINISHED); tok_t token; - while (tok.next(&token)) - { - if ((cut_at_cursor) && - (token.offset + token.text.size() >= pos)) - break; + while (tok.next(&token)) { + if ((cut_at_cursor) && (token.offset + token.text.size() >= pos)) break; - switch (token.type) - { - case TOK_STRING: - { + switch (token.type) { + case TOK_STRING: { wcstring tmp = token.text; unescape_string_in_place(&tmp, UNESCAPE_INCOMPLETE); out.append(tmp); out.push_back(L'\n'); break; } - - default: - { - break; - } + default: { break; } } } streams.out.append(out); free(buff); - } - else - { - if (cut_at_cursor) - { - end = begin+pos; + } else { + if (cut_at_cursor) { + end = begin + pos; } -// debug( 0, L"woot2 %ls -> %ls", buff, esc ); + // debug( 0, L"woot2 %ls -> %ls", buff, esc ); streams.out.append(begin, end - begin); streams.out.append(L"\n"); - } } - -/** - The commandline builtin. It is used for specifying a new value for - the commandline. -*/ -static int builtin_commandline(parser_t &parser, io_streams_t &streams, wchar_t **argv) -{ +/// The commandline builtin. It is used for specifying a new value for the commandline. +int builtin_commandline(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wgetopter_t w; - 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 append_mode = 0; int function_mode = 0; int selection_mode = 0; @@ -260,31 +196,23 @@ static int builtin_commandline(parser_t &parser, io_streams_t &streams, wchar_t int search_mode = 0; int paging_mode = 0; const wchar_t *begin = NULL, *end = NULL; - + scoped_push saved_current_buffer(¤t_buffer); scoped_push saved_current_cursor_pos(¤t_cursor_pos); - + wcstring transient_commandline; - if (get_top_transient(&transient_commandline)) - { + if (get_top_transient(&transient_commandline)) { current_buffer = transient_commandline.c_str(); current_cursor_pos = transient_commandline.size(); - } - else - { + } 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. - */ + 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; } @@ -294,173 +222,146 @@ static int builtin_commandline(parser_t &parser, io_streams_t &streams, wchar_t return 1; } - w.woptind=0; + w.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' }, - { L"selection", no_argument, 0, 's' }, - { L"paging-mode", no_argument, 0, 'P' }, - { 0, 0, 0, 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'}, + {L"selection", no_argument, 0, 's'}, + {L"paging-mode", no_argument, 0, 'P'}, + {0, 0, 0, 0}}; int opt_index = 0; - int opt = w.wgetopt_long(argc, - argv, - L"abijpctwforhI:CLSsP", - long_options, - &opt_index); - if (opt == -1) - break; + int opt = w.wgetopt_long(argc, argv, L"abijpctwforhI:CLSsP", long_options, &opt_index); + if (opt == -1) break; - switch (opt) - { - case 0: - if (long_options[opt_index].flag != 0) - break; - streams.err.append_format(BUILTIN_ERR_UNKNOWN, - argv[0], - long_options[opt_index].name); + switch (opt) { + case 0: { + if (long_options[opt_index].flag != 0) break; + streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0], + long_options[opt_index].name); builtin_print_help(parser, streams, argv[0], streams.err); return 1; - - case L'a': + } + case L'a': { append_mode = APPEND_MODE; break; - - case L'b': + } + case L'b': { buffer_part = STRING_MODE; break; - - - case L'i': + } + case L'i': { append_mode = INSERT_MODE; break; - - case L'r': + } + case L'r': { append_mode = REPLACE_MODE; break; - - case 'c': - cut_at_cursor=1; + } + case 'c': { + cut_at_cursor = 1; break; - - case 't': + } + case 't': { buffer_part = TOKEN_MODE; break; - - case 'j': + } + case 'j': { buffer_part = JOB_MODE; break; - - case 'p': + } + case 'p': { buffer_part = PROCESS_MODE; break; - - case 'f': - function_mode=1; + } + case 'f': { + function_mode = 1; break; - - case 'o': - tokenize=1; + } + case 'o': { + tokenize = 1; break; - - case 'I': + } + case 'I': { current_buffer = w.woptarg; current_cursor_pos = wcslen(w.woptarg); break; - - case 'C': + } + case 'C': { cursor_mode = 1; break; - - case 'L': + } + case 'L': { line_mode = 1; break; - - case 'S': + } + case 'S': { search_mode = 1; break; - - case 's': + } + case 's': { selection_mode = 1; break; - - case 'P': + } + case 'P': { paging_mode = 1; break; - - case 'h': + } + case 'h': { builtin_print_help(parser, streams, argv[0], streams.out); return 0; - - case L'?': - builtin_unknown_option(parser, streams, argv[0], argv[w.woptind-1]); + } + case L'?': { + builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]); return 1; + } } } - if (function_mode) - { + 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 || paging_mode) - { - streams.err.append_format(BUILTIN_ERR_COMBO, - argv[0]); + // Check for invalid switch combinations. + if (buffer_part || cut_at_cursor || append_mode || tokenize || cursor_mode || line_mode || + search_mode || paging_mode) { + streams.err.append_format(BUILTIN_ERR_COMBO, argv[0]); builtin_print_help(parser, streams, argv[0], streams.err); return 1; } - - if (argc == w.woptind) - { - streams.err.append_format(BUILTIN_ERR_MISSING, - argv[0]); + if (argc == w.woptind) { + streams.err.append_format(BUILTIN_ERR_MISSING, argv[0]); builtin_print_help(parser, streams, argv[0], streams.err); return 1; } - for (i=w.woptind; i 1)) - { - - streams.err.append_format(argv[0], - L": Too many arguments\n", - NULL); + // Check for invalid switch combinations. + if ((search_mode || line_mode || cursor_mode || paging_mode) && (argc - w.woptind > 1)) { + streams.err.append_format(argv[0], L": Too many arguments\n", NULL); builtin_print_help(parser, streams, argv[0], streams.err); return 1; } - if ((buffer_part || tokenize || cut_at_cursor) && (cursor_mode || line_mode || search_mode || paging_mode)) - { - streams.err.append_format(BUILTIN_ERR_COMBO, - argv[0]); + if ((buffer_part || tokenize || cut_at_cursor) && + (cursor_mode || line_mode || search_mode || paging_mode)) { + streams.err.append_format(BUILTIN_ERR_COMBO, argv[0]); builtin_print_help(parser, streams, argv[0], streams.err); return 1; } - - if ((tokenize || cut_at_cursor) && (argc-w.woptind)) - { - streams.err.append_format(BUILTIN_ERR_COMBO2, - argv[0], - L"--cut-at-cursor and --tokenize can not be used when setting the commandline"); - + if ((tokenize || cut_at_cursor) && (argc - w.woptind)) { + streams.err.append_format( + BUILTIN_ERR_COMBO2, argv[0], + L"--cut-at-cursor and --tokenize can not be used when setting the commandline"); builtin_print_help(parser, streams, argv[0], streams.err); return 1; } - if (append_mode && !(argc-w.woptind)) - { - streams.err.append_format(BUILTIN_ERR_COMBO2, - argv[0], - L"insertion mode switches can not be used when not in insertion mode"); + if (append_mode && !(argc - w.woptind)) { + streams.err.append_format( + BUILTIN_ERR_COMBO2, argv[0], + L"insertion mode switches can not be used when not in insertion mode"); builtin_print_help(parser, streams, argv[0], streams.err); return 1; } - /* - Set default modes - */ - if (!append_mode) - { + // Set default modes. + if (!append_mode) { append_mode = REPLACE_MODE; } - if (!buffer_part) - { + if (!buffer_part) { buffer_part = STRING_MODE; } - if (cursor_mode) - { - if (argc-w.woptind) - { + if (cursor_mode) { + if (argc - w.woptind) { wchar_t *endptr; long new_pos; errno = 0; new_pos = wcstol(argv[w.woptind], &endptr, 10); - if (*endptr || errno) - { - streams.err.append_format(BUILTIN_ERR_NOT_NUMBER, - argv[0], - argv[w.woptind]); + if (*endptr || errno) { + streams.err.append_format(BUILTIN_ERR_NOT_NUMBER, argv[0], argv[w.woptind]); builtin_print_help(parser, streams, argv[0], streams.err); } @@ -558,101 +437,63 @@ static int builtin_commandline(parser_t &parser, io_streams_t &streams, wchar_t new_pos = maxi(0L, mini(new_pos, (long)wcslen(current_buffer))); reader_set_buffer(current_buffer, (size_t)new_pos); return 0; - } - else - { - streams.out.append_format( L"%lu\n", (unsigned long)reader_get_cursor_pos()); + } else { + streams.out.append_format(L"%lu\n", (unsigned long)reader_get_cursor_pos()); return 0; } - } - if (line_mode) - { + if (line_mode) { size_t pos = reader_get_cursor_pos(); const wchar_t *buff = reader_get_buffer(); - streams.out.append_format( L"%lu\n", (unsigned long)parse_util_lineno(buff, pos)); + streams.out.append_format(L"%lu\n", (unsigned long)parse_util_lineno(buff, pos)); return 0; - } - if (search_mode) - { - return ! reader_search_mode(); + if (search_mode) { + return !reader_search_mode(); } - if (paging_mode) - { - return ! reader_has_pager_contents(); + if (paging_mode) { + return !reader_has_pager_contents(); } - - switch (buffer_part) - { - case STRING_MODE: - { + switch (buffer_part) { + case STRING_MODE: { begin = get_buffer(); - end = begin+wcslen(begin); + end = begin + wcslen(begin); break; } - - case PROCESS_MODE: - { - parse_util_process_extent(get_buffer(), - get_cursor_pos(), - &begin, - &end); + case PROCESS_MODE: { + parse_util_process_extent(get_buffer(), get_cursor_pos(), &begin, &end); break; } - - case JOB_MODE: - { - parse_util_job_extent(get_buffer(), - get_cursor_pos(), - &begin, - &end); + case JOB_MODE: { + parse_util_job_extent(get_buffer(), get_cursor_pos(), &begin, &end); break; } - - case TOKEN_MODE: - { - parse_util_token_extent(get_buffer(), - get_cursor_pos(), - &begin, - &end, - 0, 0); + case TOKEN_MODE: { + parse_util_token_extent(get_buffer(), get_cursor_pos(), &begin, &end, 0, 0); break; } - } - switch (argc-w.woptind) - { - case 0: - { + switch (argc - w.woptind) { + case 0: { write_part(begin, end, cut_at_cursor, tokenize, streams); break; } - - case 1: - { + case 1: { replace_part(begin, end, argv[w.woptind], append_mode); break; } - - default: - { + default: { wcstring sb = argv[w.woptind]; - int i; - - for (i=w.woptind+1; i +#include + +class parser_t; + +int builtin_commandline(parser_t &parser, io_streams_t &streams, wchar_t **argv); +#endif diff --git a/src/builtin_complete.cpp b/src/builtin_complete.cpp index bd117a8fa..90fc47146 100644 --- a/src/builtin_complete.cpp +++ b/src/builtin_complete.cpp @@ -1,237 +1,136 @@ -/** \file builtin_complete.c Functions defining the complete builtin - -Functions used for implementing the complete builtin. -*/ +// Functions used for implementing the complete builtin. +#include #include #include +#include // IWYU pragma: keep #include #include -#include // IWYU pragma: keep -#include -#include "fallback.h" // IWYU pragma: keep #include "builtin.h" #include "common.h" #include "complete.h" -#include "wgetopt.h" -#include "parser.h" -#include "reader.h" #include "env.h" +#include "fallback.h" // IWYU pragma: keep #include "io.h" #include "parse_constants.h" -#include "proc.h" #include "parse_util.h" +#include "parser.h" +#include "proc.h" +#include "reader.h" +#include "wgetopt.h" #include "wutil.h" // IWYU pragma: keep -/* - builtin_complete_* are a set of rather silly looping functions that - make sure that all the proper combinations of complete_add or - complete_remove get called. This is needed since complete allows you - to specify multiple switches on a single commandline, like 'complete - -s a -s b -s c', but the complete_add function only accepts one - short switch and one long switch. -*/ +// builtin_complete_* are a set of rather silly looping functions that make sure that all the proper +// combinations of complete_add or complete_remove get called. This is needed since complete allows +// you to specify multiple switches on a single commandline, like 'complete -s a -s b -s c', but the +// complete_add function only accepts one short switch and one long switch. -/** - Silly function -*/ -static void builtin_complete_add2(const wchar_t *cmd, - int cmd_type, - const wchar_t *short_opt, - const wcstring_list_t &gnu_opt, - const wcstring_list_t &old_opt, - int result_mode, - const wchar_t *condition, - const wchar_t *comp, - const wchar_t *desc, - int flags) -{ +/// Silly function. +static void builtin_complete_add2(const wchar_t *cmd, int cmd_type, const wchar_t *short_opt, + const wcstring_list_t &gnu_opt, const wcstring_list_t &old_opt, + int result_mode, const wchar_t *condition, const wchar_t *comp, + const wchar_t *desc, int flags) { size_t i; const wchar_t *s; - for (s=short_opt; *s; s++) - { - complete_add(cmd, - cmd_type, - wcstring(1, *s), - option_type_short, - result_mode, - condition, - comp, - desc, - flags); + for (s = short_opt; *s; s++) { + complete_add(cmd, cmd_type, wcstring(1, *s), option_type_short, result_mode, condition, + comp, desc, flags); } - for (i=0; i comp; - complete(do_complete_param, &comp, COMPLETION_REQUEST_DEFAULT, env_vars_snapshot_t::current()); + complete(do_complete_param, &comp, COMPLETION_REQUEST_DEFAULT, + env_vars_snapshot_t::current()); - for (size_t i=0; i< comp.size() ; i++) - { - const completion_t &next = comp.at(i); + for (size_t i = 0; i < comp.size(); i++) { + const completion_t &next = comp.at(i); - /* Make a fake commandline, and then apply the completion to it. */ + // Make a fake commandline, and then apply the completion to it. const wcstring faux_cmdline = token; size_t tmp_cursor = faux_cmdline.size(); - wcstring faux_cmdline_with_completion = completion_apply_to_command_line(next.completion, next.flags, faux_cmdline, &tmp_cursor, false); + wcstring faux_cmdline_with_completion = completion_apply_to_command_line( + next.completion, next.flags, faux_cmdline, &tmp_cursor, false); - /* completion_apply_to_command_line will append a space unless COMPLETE_NO_SPACE is set. We don't want to set COMPLETE_NO_SPACE because that won't close quotes. What we want is to close the quote, but not append the space. So we just look for the space and clear it. */ - if (!(next.flags & COMPLETE_NO_SPACE) && string_suffixes_string(L" ", faux_cmdline_with_completion)) - { - faux_cmdline_with_completion.resize(faux_cmdline_with_completion.size() - 1); + // completion_apply_to_command_line will append a space unless COMPLETE_NO_SPACE + // is set. We don't want to set COMPLETE_NO_SPACE because that won't close + // quotes. What we want is to close the quote, but not append the space. So we + // just look for the space and clear it. + if (!(next.flags & COMPLETE_NO_SPACE) && + string_suffixes_string(L" ", faux_cmdline_with_completion)) { + faux_cmdline_with_completion.resize(faux_cmdline_with_completion.size() - + 1); } - /* The input data is meant to be something like you would have on the command line, e.g. includes backslashes. The output should be raw, i.e. unescaped. So we need to unescape the command line. See #1127 */ + // The input data is meant to be something like you would have on the command + // line, e.g. includes backslashes. The output should be raw, i.e. unescaped. So + // we need to unescape the command line. See #1127. unescape_string_in_place(&faux_cmdline_with_completion, UNESCAPE_DEFAULT); streams.out.append(faux_cmdline_with_completion); - /* Append any description */ - if (! next.description.empty()) - { + // Append any description. + if (!next.description.empty()) { streams.out.push_back(L'\t'); streams.out.append(next.description); } @@ -487,58 +358,32 @@ static int builtin_complete(parser_t &parser, io_streams_t &streams, wchar_t **a recursion_level--; } - } - else if (w.woptind != argc) - { - streams.err.append_format(_(L"%ls: Too many arguments\n"), - argv[0]); + } else if (w.woptind != argc) { + streams.err.append_format(_(L"%ls: Too many arguments\n"), argv[0]); builtin_print_help(parser, streams, argv[0], streams.err); res = true; - } - else if (cmd.empty() && path.empty()) - { - /* No arguments specified, meaning we print the definitions of - * all specified completions to stdout.*/ + } else if (cmd.empty() && path.empty()) { + // No arguments specified, meaning we print the definitions of all specified completions + // to stdout. streams.out.append(complete_print()); - } - else - { + } else { int flags = COMPLETE_AUTO_SPACE; - - if (remove) - { - builtin_complete_remove(cmd, - path, - short_opt.c_str(), - gnu_opt, - old_opt); - + + 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); } - else - { - builtin_complete_add(cmd, - path, - short_opt.c_str(), - gnu_opt, - old_opt, - result_mode, - authoritative, - condition, - comp, - desc, - flags); - } - - // Handle wrap targets (probably empty) - // We only wrap commands, not paths - for (size_t w=0; w < wrap_targets.size(); w++) - { + + // Handle wrap targets (probably empty). We only wrap commands, not paths. + for (size_t w = 0; w < wrap_targets.size(); w++) { const wcstring &wrap_target = wrap_targets.at(w); - for (size_t i=0; i < cmd.size(); i++) - { - - (remove ? complete_remove_wrapper : complete_add_wrapper)(cmd.at(i), wrap_target); + for (size_t i = 0; i < cmd.size(); i++) { + (remove ? complete_remove_wrapper : complete_add_wrapper)(cmd.at(i), + wrap_target); } } } diff --git a/src/builtin_complete.h b/src/builtin_complete.h new file mode 100644 index 000000000..3ef45a32d --- /dev/null +++ b/src/builtin_complete.h @@ -0,0 +1,11 @@ +// Prototypes for functions for executing builtin_complete functions. +#ifndef FISH_BUILTIN_COMPLETE_H +#define FISH_BUILTIN_COMPLETE_H + +#include +#include + +class parser_t; + +int builtin_complete(parser_t &parser, io_streams_t &streams, wchar_t **argv); +#endif diff --git a/src/builtin_jobs.cpp b/src/builtin_jobs.cpp index 419827693..406795aa3 100644 --- a/src/builtin_jobs.cpp +++ b/src/builtin_jobs.cpp @@ -1,6 +1,4 @@ -/** \file builtin_jobs.c - Functions for executing the jobs builtin. -*/ +// Functions for executing the jobs builtin. #include "config.h" // IWYU pragma: keep #include @@ -9,75 +7,53 @@ #include #endif -#include "fallback.h" // IWYU pragma: keep -#include "wutil.h" #include "builtin.h" -#include "proc.h" #include "common.h" -#include "wgetopt.h" +#include "fallback.h" // IWYU pragma: keep #include "io.h" +#include "proc.h" +#include "wgetopt.h" +#include "wutil.h" // IWYU pragma: keep class parser_t; -/** - Print modes for the jobs builtin -*/ -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 */ -} -; - - +/// Print modes for the jobs builtin. +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 +}; #ifdef HAVE__PROC_SELF_STAT -/** - Calculates the cpu usage (in percent) of the specified job. -*/ -static int cpu_use(const job_t *j) -{ - double u=0; +/// Calculates the cpu usage (in percent) of the specified job. +static int cpu_use(const job_t *j) { + double u = 0; process_t *p; - for (p=j->first_process; p; p=p->next) - { + 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 ); - */ - - u += ((double)(jiffies-p->last_jiffies))/(t2-t1); + // 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; + return u * 1000000; } #endif -/** - Print information about the specified job -*/ -static void builtin_jobs_print(const job_t *j, int mode, int header, io_streams_t &streams) -{ +/// Print information about the specified job. +static void builtin_jobs_print(const job_t *j, int mode, int header, io_streams_t &streams) { process_t *p; - switch (mode) - { - case JOBS_DEFAULT: - { - - if (header) - { - /* - Print table header before first job - */ + switch (mode) { + case JOBS_DEFAULT: { + if (header) { + // Print table header before first job. streams.out.append(_(L"Job\tGroup\t")); #ifdef HAVE__PROC_SELF_STAT streams.out.append(_(L"CPU\t")); @@ -85,234 +61,149 @@ static void builtin_jobs_print(const job_t *j, int mode, int header, io_streams_ streams.out.append(_(L"State\tCommand\n")); } - streams.out.append_format( L"%d\t%d\t", j->job_id, j->pgid); + streams.out.append_format(L"%d\t%d\t", j->job_id, j->pgid); #ifdef HAVE__PROC_SELF_STAT - streams.out.append_format( L"%d%%\t", cpu_use(j)); + streams.out.append_format(L"%d%%\t", cpu_use(j)); #endif - streams.out.append(job_is_stopped(j)?_(L"stopped"):_(L"running")); + streams.out.append(job_is_stopped(j) ? _(L"stopped") : _(L"running")); streams.out.append(L"\t"); streams.out.append(j->command_wcstr()); streams.out.append(L"\n"); break; } - - case JOBS_PRINT_GROUP: - { - if (header) - { - /* - Print table header before first job - */ + case JOBS_PRINT_GROUP: { + if (header) { + // Print table header before first job. streams.out.append(_(L"Group\n")); } - streams.out.append_format( L"%d\n", j->pgid); + streams.out.append_format(L"%d\n", j->pgid); break; } - - case JOBS_PRINT_PID: - { - if (header) - { - /* - Print table header before first job - */ + case JOBS_PRINT_PID: { + if (header) { + // Print table header before first job. streams.out.append(_(L"Process\n")); } - for (p=j->first_process; p; p=p->next) - { - streams.out.append_format( L"%d\n", p->pid); + for (p = j->first_process; p; p = p->next) { + streams.out.append_format(L"%d\n", p->pid); } break; } - - case JOBS_PRINT_COMMAND: - { - if (header) - { - /* - Print table header before first job - */ + case JOBS_PRINT_COMMAND: { + if (header) { + // Print table header before first job. streams.out.append(_(L"Command\n")); } - for (p=j->first_process; p; p=p->next) - { - streams.out.append_format( L"%ls\n", p->argv0()); + for (p = j->first_process; p; p = p->next) { + streams.out.append_format(L"%ls\n", p->argv0()); } break; } } - } - - -/** - The jobs builtin. Used fopr printing running jobs. Defined in builtin_jobs.c. -*/ -static int builtin_jobs(parser_t &parser, io_streams_t &streams, wchar_t **argv) -{ +/// The jobs builtin. Used fopr printing running jobs. Defined in builtin_jobs.c. +int builtin_jobs(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wgetopter_t w; - int argc=0; - int found=0; - int mode=JOBS_DEFAULT; + int argc = 0; + int found = 0; + int mode = JOBS_DEFAULT; int print_last = 0; argc = builtin_count_args(argv); - w.woptind=0; + w.woptind = 0; - while (1) - { - static const struct woption - long_options[] = - { - { - L"pid", no_argument, 0, 'p' - } - , - { - L"command", no_argument, 0, 'c' - } - , - { - L"group", no_argument, 0, 'g' - } - , - { - L"last", no_argument, 0, 'l' - } - , - { - L"help", no_argument, 0, 'h' - } - , - { - 0, 0, 0, 0 - } - } - ; + while (1) { + static const struct woption long_options[] = { + {L"pid", no_argument, 0, 'p'}, {L"command", no_argument, 0, 'c'}, + {L"group", no_argument, 0, 'g'}, {L"last", no_argument, 0, 'l'}, + {L"help", no_argument, 0, 'h'}, {0, 0, 0, 0}}; int opt_index = 0; - int opt = w.wgetopt_long(argc, - argv, - L"pclgh", - long_options, - &opt_index); - if (opt == -1) - break; + int opt = w.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; - streams.err.append_format(BUILTIN_ERR_UNKNOWN, - argv[0], - long_options[opt_index].name); + switch (opt) { + case 0: { + if (long_options[opt_index].flag != 0) break; + streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0], + long_options[opt_index].name); builtin_print_help(parser, streams, argv[0], streams.err); - - return 1; - - - case 'p': - mode=JOBS_PRINT_PID; + } + case 'p': { + mode = JOBS_PRINT_PID; break; - - case 'c': - mode=JOBS_PRINT_COMMAND; + } + case 'c': { + mode = JOBS_PRINT_COMMAND; break; - - case 'g': - mode=JOBS_PRINT_GROUP; + } + case 'g': { + mode = JOBS_PRINT_GROUP; break; - - case 'l': - { + } + case 'l': { print_last = 1; break; } - - case 'h': + case 'h': { builtin_print_help(parser, streams, argv[0], streams.out); return 0; - - case '?': - builtin_unknown_option(parser, streams, argv[0], argv[w.woptind-1]); + } + case '?': { + builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]); return 1; - + } } } - if (print_last) - { - /* - Ignore unconstructed jobs, i.e. ourself. - */ + if (print_last) { + // Ignore unconstructed jobs, i.e. ourself. job_iterator_t jobs; const job_t *j; - while ((j = jobs.next())) - { - - if ((j->flags & JOB_CONSTRUCTED) && !job_is_completed(j)) - { + while ((j = jobs.next())) { + if ((j->flags & JOB_CONSTRUCTED) && !job_is_completed(j)) { builtin_jobs_print(j, mode, !streams.out_is_redirected, streams); return 0; } } - } - else - { - if (w.woptind < argc) - { + } else { + if (w.woptind < argc) { int i; - for (i=w.woptind; iflags & JOB_CONSTRUCTED) && !job_is_completed(j)) - { + while ((j = jobs.next())) { + // Ignore unconstructed jobs, i.e. ourself. + if ((j->flags & JOB_CONSTRUCTED) && !job_is_completed(j)) { builtin_jobs_print(j, mode, !streams.out_is_redirected, streams); found = 1; } @@ -320,20 +211,13 @@ static int builtin_jobs(parser_t &parser, io_streams_t &streams, wchar_t **argv) } } - if (!found) - { - /* - Do not babble if not interactive - */ - if (!streams.out_is_redirected) - { - streams.out.append_format( - _(L"%ls: There are no jobs\n"), - argv[0]); + if (!found) { + // Do not babble if not interactive. + if (!streams.out_is_redirected) { + streams.out.append_format(_(L"%ls: There are no jobs\n"), argv[0]); } return 1; } return 0; } - diff --git a/src/builtin_jobs.h b/src/builtin_jobs.h new file mode 100644 index 000000000..caf059f88 --- /dev/null +++ b/src/builtin_jobs.h @@ -0,0 +1,11 @@ +// Prototypes for functions for executing builtin_jobs functions. +#ifndef FISH_BUILTIN_JOBS_H +#define FISH_BUILTIN_JOBS_H + +#include +#include + +class parser_t; + +int builtin_jobs(parser_t &parser, io_streams_t &streams, wchar_t **argv); +#endif diff --git a/src/builtin_printf.cpp b/src/builtin_printf.cpp index 97427948f..a3df00d39 100644 --- a/src/builtin_printf.cpp +++ b/src/builtin_printf.cpp @@ -1,94 +1,91 @@ -/* printf - format and print data - Copyright (C) 1990-2007 Free Software Foundation, Inc. +// printf - format and print data +// Copyright (C) 1990-2007 Free Software Foundation, Inc. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. +// Usage: printf format [argument...] +// +// A front end to the printf function that lets it be used from the shell. +// +// Backslash escapes: +// +// \" = double quote +// \\ = backslash +// \a = alert (bell) +// \b = backspace +// \c = produce no further output +// \e = escape +// \f = form feed +// \n = new line +// \r = carriage return +// \t = horizontal tab +// \v = vertical tab +// \ooo = octal number (ooo is 1 to 3 digits) +// \xhh = hexadecimal number (hhh is 1 to 2 digits) +// \uhhhh = 16-bit Unicode character (hhhh is 4 digits) +// \Uhhhhhhhh = 32-bit Unicode character (hhhhhhhh is 8 digits) +// +// Additional directive: +// +// %b = print an argument string, interpreting backslash escapes, +// except that octal escapes are of the form \0 or \0ooo. +// +// The `format' argument is re-used as many times as necessary +// to convert all of the given arguments. +// +// David MacKenzie - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - -/* Usage: printf format [argument...] - - A front end to the printf function that lets it be used from the shell. - - Backslash escapes: - - \" = double quote - \\ = backslash - \a = alert (bell) - \b = backspace - \c = produce no further output - \e = escape - \f = form feed - \n = new line - \r = carriage return - \t = horizontal tab - \v = vertical tab - \ooo = octal number (ooo is 1 to 3 digits) - \xhh = hexadecimal number (hhh is 1 to 2 digits) - \uhhhh = 16-bit Unicode character (hhhh is 4 digits) - \Uhhhhhhhh = 32-bit Unicode character (hhhhhhhh is 8 digits) - - Additional directive: - - %b = print an argument string, interpreting backslash escapes, - except that octal escapes are of the form \0 or \0ooo. - - The `format' argument is re-used as many times as necessary - to convert all of the given arguments. - - David MacKenzie */ - -/* This file has been imported from source code of printf command in GNU Coreutils version 6.9 */ +// This file has been imported from source code of printf command in GNU Coreutils version 6.9. #include #include #include +#include +#include +#include +#include +#include #include #include -#include -#include -#include -#include -#include +#include "builtin.h" #include "common.h" #include "io.h" -#include "wutil.h" // IWYU pragma: keep #include "proc.h" -#include "builtin.h" +#include "wutil.h" // IWYU pragma: keep class parser_t; -struct builtin_printf_state_t -{ - /* Out and err streams. Note this is a captured reference! */ +struct builtin_printf_state_t { + // Out and err streams. Note this is a captured reference! io_streams_t &streams; - - /* The status of the operation */ + + // The status of the operation. int exit_code; - /* Whether we should stop outputting. This gets set in the case of an error, and also with the \c escape. */ + // Whether we should stop outputting. This gets set in the case of an error, and also with the + // \c escape. bool early_exit; - explicit builtin_printf_state_t(io_streams_t &s) : streams(s), exit_code(0), early_exit(false) - { - } + explicit builtin_printf_state_t(io_streams_t &s) + : streams(s), exit_code(0), early_exit(false) {} void verify_numeric(const wchar_t *s, const wchar_t *end, int errcode); - void print_direc(const wchar_t *start, size_t length, wchar_t conversion, - bool have_field_width, int field_width, - bool have_precision, int precision, - wchar_t const *argument); + void print_direc(const wchar_t *start, size_t length, wchar_t conversion, bool have_field_width, + int field_width, bool have_precision, int precision, wchar_t const *argument); int print_formatted(const wchar_t *format, int argc, wchar_t **argv); @@ -103,155 +100,152 @@ struct builtin_printf_state_t void append_format_output(const wchar_t *fmt, ...); }; -static bool is_octal_digit(wchar_t c) -{ - return c != L'\0' && wcschr(L"01234567", c) != NULL; -} +static bool is_octal_digit(wchar_t c) { return c != L'\0' && wcschr(L"01234567", c) != NULL; } -static bool is_hex_digit(wchar_t c) -{ +static bool is_hex_digit(wchar_t c) { return c != L'\0' && wcschr(L"0123456789ABCDEFabcdef", c) != NULL; } -static int hex_to_bin(const wchar_t &c) -{ - switch (c) - { - case L'0': +static int hex_to_bin(const wchar_t &c) { + switch (c) { + case L'0': { return 0; - case L'1': + } + case L'1': { return 1; - case L'2': + } + case L'2': { return 2; - case L'3': + } + case L'3': { return 3; - case L'4': + } + case L'4': { return 4; - case L'5': + } + case L'5': { return 5; - case L'6': + } + case L'6': { return 6; - case L'7': + } + case L'7': { return 7; - case L'8': + } + case L'8': { return 8; - case L'9': + } + case L'9': { return 9; + } case L'a': - case L'A': + case L'A': { return 10; + } case L'b': - case L'B': + case L'B': { return 11; + } case L'c': - case L'C': + case L'C': { return 12; + } case L'd': - case L'D': + case L'D': { return 13; + } case L'e': - case L'E': + case L'E': { return 14; + } case L'f': - case L'F': + case L'F': { return 15; - default: - return -1; + } + default: { return -1; } } } -static int octal_to_bin(wchar_t c) -{ - switch (c) - { - case L'0': +static int octal_to_bin(wchar_t c) { + switch (c) { + case L'0': { return 0; - case L'1': + } + case L'1': { return 1; - case L'2': + } + case L'2': { return 2; - case L'3': + } + case L'3': { return 3; - case L'4': + } + case L'4': { return 4; - case L'5': + } + case L'5': { return 5; - case L'6': + } + case L'6': { return 6; - case L'7': + } + case L'7': { return 7; - default: - return -1; + } + default: { return -1; } } } -/* This message appears in N_() here rather than just in _() below because - the sole use would have been in a #define. */ -static wchar_t const *const cfcc_msg = - N_(L"warning: %ls: character(s) following character constant have been ignored"); - -double C_STRTOD(wchar_t const *nptr, wchar_t **endptr) -{ +double C_STRTOD(wchar_t const *nptr, wchar_t **endptr) { double r; const wcstring saved_locale = wsetlocale(LC_NUMERIC, NULL); - if (!saved_locale.empty()) - { + if (!saved_locale.empty()) { wsetlocale(LC_NUMERIC, L"C"); } r = wcstod(nptr, endptr); - if (!saved_locale.empty()) - { + if (!saved_locale.empty()) { wsetlocale(LC_NUMERIC, saved_locale.c_str()); } return r; } -void builtin_printf_state_t::fatal_error(const wchar_t *fmt, ...) -{ - // Don't error twice - if (early_exit) - return; +void builtin_printf_state_t::fatal_error(const wchar_t *fmt, ...) { + // Don't error twice. + if (early_exit) return; va_list va; va_start(va, fmt); wcstring errstr = vformat_string(fmt, va); va_end(va); streams.err.append(errstr); - if (! string_suffixes_string(L"\n", errstr)) - streams.err.push_back(L'\n'); + if (!string_suffixes_string(L"\n", errstr)) streams.err.push_back(L'\n'); this->exit_code = STATUS_BUILTIN_ERROR; this->early_exit = true; } -void builtin_printf_state_t::append_output(wchar_t c) -{ - // Don't output if we're done - if (early_exit) - return; +void builtin_printf_state_t::append_output(wchar_t c) { + // Don't output if we're done. + if (early_exit) return; streams.out.push_back(c); } -void builtin_printf_state_t::append_output(const wchar_t *c) -{ - // Don't output if we're done - if (early_exit) - return; +void builtin_printf_state_t::append_output(const wchar_t *c) { + // Don't output if we're done. + if (early_exit) return; streams.out.append(c); } -void builtin_printf_state_t::append_format_output(const wchar_t *fmt, ...) -{ - // Don't output if we're done - if (early_exit) - return; +void builtin_printf_state_t::append_format_output(const wchar_t *fmt, ...) { + // Don't output if we're done. + if (early_exit) return; va_list va; va_start(va, fmt); @@ -260,15 +254,10 @@ void builtin_printf_state_t::append_format_output(const wchar_t *fmt, ...) streams.out.append(tmp); } - -void builtin_printf_state_t::verify_numeric(const wchar_t *s, const wchar_t *end, int errcode) -{ - if (errcode != 0) - { +void builtin_printf_state_t::verify_numeric(const wchar_t *s, const wchar_t *end, int errcode) { + if (errcode != 0) { this->fatal_error(L"%ls: %s", s, strerror(errcode)); - } - else if (*end) - { + } else if (*end) { if (s == end) this->fatal_error(_(L"%ls: expected a numeric value"), s); else @@ -276,39 +265,33 @@ void builtin_printf_state_t::verify_numeric(const wchar_t *s, const wchar_t *end } } -template -static T raw_string_to_scalar_type(const wchar_t *s, wchar_t ** end); +template +static T raw_string_to_scalar_type(const wchar_t *s, wchar_t **end); -// we use wcstoll instead of wcstoimax because FreeBSD 8 has busted wcstoumax and wcstoimax - see #626 -template<> -intmax_t raw_string_to_scalar_type(const wchar_t *s, wchar_t ** end) -{ +// we use wcstoll instead of wcstoimax because FreeBSD 8 has busted wcstoumax and wcstoimax - see +// #626 +template <> +intmax_t raw_string_to_scalar_type(const wchar_t *s, wchar_t **end) { return wcstoll(s, end, 0); } -template<> -uintmax_t raw_string_to_scalar_type(const wchar_t *s, wchar_t ** end) -{ +template <> +uintmax_t raw_string_to_scalar_type(const wchar_t *s, wchar_t **end) { return wcstoull(s, end, 0); } -template<> -long double raw_string_to_scalar_type(const wchar_t *s, wchar_t ** end) -{ +template <> +long double raw_string_to_scalar_type(const wchar_t *s, wchar_t **end) { return C_STRTOD(s, end); } -template -static T string_to_scalar_type(const wchar_t *s, builtin_printf_state_t *state) -{ +template +static T string_to_scalar_type(const wchar_t *s, builtin_printf_state_t *state) { T val; - if (*s == L'\"' || *s == L'\'') - { + if (*s == L'\"' || *s == L'\'') { wchar_t ch = *++s; val = ch; - } - else - { + } else { wchar_t *end = NULL; errno = 0; val = raw_string_to_scalar_type(s, &end); @@ -317,91 +300,85 @@ static T string_to_scalar_type(const wchar_t *s, builtin_printf_state_t *state) return val; } -/* Output a single-character \ escape. */ - -void builtin_printf_state_t::print_esc_char(wchar_t c) -{ - switch (c) - { - case L'a': /* Alert. */ +/// Output a single-character \ escape. +void builtin_printf_state_t::print_esc_char(wchar_t c) { + switch (c) { + case L'a': { // alert this->append_output(L'\a'); break; - case L'b': /* Backspace. */ + } + case L'b': { // backspace this->append_output(L'\b'); break; - case L'c': /* Cancel the rest of the output. */ + } + case L'c': { // cancel the rest of the output this->early_exit = true; break; - case L'e': /* Escape */ + } + case L'e': { // escape this->append_output(L'\x1B'); break; - case L'f': /* Form feed. */ + } + case L'f': { // form feed this->append_output(L'\f'); break; - case L'n': /* New line. */ + } + case L'n': { // new line this->append_output(L'\n'); break; - case L'r': /* Carriage return. */ + } + case L'r': { // carriage return this->append_output(L'\r'); break; - case L't': /* Horizontal tab. */ + } + case L't': { // horizontal tab this->append_output(L'\t'); break; - case L'v': /* Vertical tab. */ + } + case L'v': { // vertical tab this->append_output(L'\v'); break; - default: + } + default: { this->append_output(c); break; + } } } -/* Print a \ escape sequence starting at ESCSTART. - Return the number of characters in the escape sequence - besides the backslash. - If OCTAL_0 is nonzero, octal escapes are of the form \0ooo, where o - is an octal digit; otherwise they are of the form \ooo. */ -long builtin_printf_state_t::print_esc(const wchar_t *escstart, bool octal_0) -{ +/// Print a \ escape sequence starting at ESCSTART. +/// Return the number of characters in the escape sequence besides the backslash.. +/// If OCTAL_0 is nonzero, octal escapes are of the form \0ooo, where o +/// is an octal digit; otherwise they are of the form \ooo. +long builtin_printf_state_t::print_esc(const wchar_t *escstart, bool octal_0) { const wchar_t *p = escstart + 1; - int esc_value = 0; /* Value of \nnn escape. */ - int esc_length; /* Length of \nnn escape. */ + int esc_value = 0; /* Value of \nnn escape. */ + int esc_length; /* Length of \nnn escape. */ - if (*p == L'x') - { - /* A hexadecimal \xhh escape sequence must have 1 or 2 hex. digits. */ + if (*p == L'x') { + // A hexadecimal \xhh escape sequence must have 1 or 2 hex. digits. for (esc_length = 0, ++p; esc_length < 2 && is_hex_digit(*p); ++esc_length, ++p) esc_value = esc_value * 16 + hex_to_bin(*p); - if (esc_length == 0) - this->fatal_error(_(L"missing hexadecimal number in escape")); + if (esc_length == 0) this->fatal_error(_(L"missing hexadecimal number in escape")); this->append_output(ENCODE_DIRECT_BASE + esc_value % 256); - } - else if (is_octal_digit(*p)) - { - /* Parse \0ooo (if octal_0 && *p == L'0') or \ooo (otherwise). - Allow \ooo if octal_0 && *p != L'0'; this is an undocumented - extension to POSIX that is compatible with Bash 2.05b. */ - /* Wrap mod 256, which matches historic behavior */ - for (esc_length = 0, p += octal_0 && *p == L'0'; esc_length < 3 && is_octal_digit(*p); ++esc_length, ++p) + } else if (is_octal_digit(*p)) { + // Parse \0ooo (if octal_0 && *p == L'0') or \ooo (otherwise). Allow \ooo if octal_0 && *p + // != L'0'; this is an undocumented extension to POSIX that is compatible with Bash 2.05b. + // Wrap mod 256, which matches historic behavior. + for (esc_length = 0, p += octal_0 && *p == L'0'; esc_length < 3 && is_octal_digit(*p); + ++esc_length, ++p) esc_value = esc_value * 8 + octal_to_bin(*p); this->append_output(ENCODE_DIRECT_BASE + esc_value % 256); - } - else if (*p && wcschr(L"\"\\abcefnrtv", *p)) - { + } else if (*p && wcschr(L"\"\\abcefnrtv", *p)) { print_esc_char(*p++); - } - else if (*p == L'u' || *p == L'U') - { + } else if (*p == L'u' || *p == L'U') { wchar_t esc_char = *p; p++; uint32_t uni_value = 0; - for (size_t esc_length = 0; esc_length < (esc_char == L'u' ? 4 : 8); esc_length++) - { - if (! is_hex_digit(*p)) - { - /* Escape sequence must be done. Complain if we didn't get anything */ - if (esc_length == 0) - { + for (size_t esc_length = 0; esc_length < (esc_char == L'u' ? 4 : 8); esc_length++) { + if (!is_hex_digit(*p)) { + // Escape sequence must be done. Complain if we didn't get anything. + if (esc_length == 0) { this->fatal_error(_(L"Missing hexadecimal number in Unicode escape")); } break; @@ -410,28 +387,27 @@ long builtin_printf_state_t::print_esc(const wchar_t *escstart, bool octal_0) p++; } - /* PCA GNU printf respects the limitations described in ISO N717, about which universal characters "shall not" be specified. I believe this limitation is for the benefit of compilers; I see no reason to impose it in builtin_printf. - - If __STDC_ISO_10646__ is defined, then it means wchar_t can and does hold Unicode code points, so just use that. If not defined, use the %lc printf conversion; this probably won't do anything good if your wide character set is not Unicode, but such platforms are exceedingly rare. - */ - if (uni_value > 0x10FFFF) - { - this->fatal_error(_(L"Unicode character out of range: \\%c%0*x"), esc_char, (esc_char == L'u' ? 4 : 8), uni_value); - } - else - { + // PCA GNU printf respects the limitations described in ISO N717, about which universal + // characters "shall not" be specified. I believe this limitation is for the benefit of + // compilers; I see no reason to impose it in builtin_printf. + // + // If __STDC_ISO_10646__ is defined, then it means wchar_t can and does hold Unicode code + // points, so just use that. If not defined, use the %lc printf conversion; this probably + // won't do anything good if your wide character set is not Unicode, but such platforms are + // exceedingly rare. + if (uni_value > 0x10FFFF) { + this->fatal_error(_(L"Unicode character out of range: \\%c%0*x"), esc_char, + (esc_char == L'u' ? 4 : 8), uni_value); + } else { #if defined(__STDC_ISO_10646__) this->append_output(uni_value); #else this->append_format_output(L"%lc", uni_value); #endif } - } - else - { + } else { this->append_output(L'\\'); - if (*p) - { + if (*p) { this->append_output(*p); p++; } @@ -439,10 +415,8 @@ long builtin_printf_state_t::print_esc(const wchar_t *escstart, bool octal_0) return p - escstart - 1; } -/* Print string STR, evaluating \ escapes. */ - -void builtin_printf_state_t::print_esc_string(const wchar_t *str) -{ +/// Print string STR, evaluating \ escapes. +void builtin_printf_state_t::print_esc_string(const wchar_t *str) { for (; *str; str++) if (*str == L'\\') str += print_esc(str, true); @@ -450,30 +424,27 @@ void builtin_printf_state_t::print_esc_string(const wchar_t *str) this->append_output(*str); } -/* Evaluate a printf conversion specification. START is the start of - the directive, LENGTH is its length, and CONVERSION specifies the - type of conversion. LENGTH does not include any length modifier or - the conversion specifier itself. FIELD_WIDTH and PRECISION are the - field width and precision for '*' values, if HAVE_FIELD_WIDTH and - HAVE_PRECISION are true, respectively. ARGUMENT is the argument to - be formatted. */ - +/// Evaluate a printf conversion specification. START is the start of the directive, LENGTH is its +/// length, and CONVERSION specifies the type of conversion. LENGTH does not include any length +/// modifier or the conversion specifier itself. FIELD_WIDTH and PRECISION are the field width and +/// precision for '*' values, if HAVE_FIELD_WIDTH and HAVE_PRECISION are true, respectively. +/// ARGUMENT is the argument to be formatted. void builtin_printf_state_t::print_direc(const wchar_t *start, size_t length, wchar_t conversion, - bool have_field_width, int field_width, - bool have_precision, int precision, - wchar_t const *argument) -{ - // Start with everything except the conversion specifier + bool have_field_width, int field_width, + bool have_precision, int precision, + wchar_t const *argument) { + // Start with everything except the conversion specifier. wcstring fmt(start, length); - /* Create a copy of the % directive, with an intmax_t-wide width modifier substituted for any existing integer length modifier. */ - switch (conversion) - { + // Create a copy of the % directive, with an intmax_t-wide width modifier substituted for any + // existing integer length modifier. + switch (conversion) { case L'd': case L'i': - case L'u': + case L'u': { fmt.append(L"ll"); break; + } case L'a': case L'e': case L'f': @@ -481,66 +452,56 @@ void builtin_printf_state_t::print_direc(const wchar_t *start, size_t length, wc case L'A': case L'E': case L'F': - case L'G': + case L'G': { fmt.append(L"L"); break; + } case L's': - case L'c': + case L'c': { fmt.append(L"l"); break; - default: - break; + } + default: { break; } } - // Append the conversion itself + // Append the conversion itself. fmt.push_back(conversion); - switch (conversion) - { + switch (conversion) { case L'd': - case L'i': - { + case L'i': { intmax_t arg = string_to_scalar_type(argument, this); - if (! have_field_width) - { - if (! have_precision) + if (!have_field_width) { + if (!have_precision) this->append_format_output(fmt.c_str(), arg); else this->append_format_output(fmt.c_str(), precision, arg); - } - else - { - if (! have_precision) + } else { + if (!have_precision) this->append_format_output(fmt.c_str(), field_width, arg); else this->append_format_output(fmt.c_str(), field_width, precision, arg); } + break; } - break; - case L'o': case L'u': case L'x': - case L'X': - { + case L'X': { uintmax_t arg = string_to_scalar_type(argument, this); - if (!have_field_width) - { + if (!have_field_width) { if (!have_precision) this->append_format_output(fmt.c_str(), arg); else this->append_format_output(fmt.c_str(), precision, arg); - } - else - { + } else { if (!have_precision) this->append_format_output(fmt.c_str(), field_width, arg); else this->append_format_output(fmt.c_str(), field_width, precision, arg); } + break; } - break; - case L'a': case L'A': case L'e': @@ -548,98 +509,81 @@ void builtin_printf_state_t::print_direc(const wchar_t *start, size_t length, wc case L'f': case L'F': case L'g': - case L'G': - { + case L'G': { long double arg = string_to_scalar_type(argument, this); - if (!have_field_width) - { + if (!have_field_width) { if (!have_precision) this->append_format_output(fmt.c_str(), arg); else this->append_format_output(fmt.c_str(), precision, arg); - } - else - { + } else { if (!have_precision) this->append_format_output(fmt.c_str(), field_width, arg); else this->append_format_output(fmt.c_str(), field_width, precision, arg); } + break; } - break; - - case L'c': + case L'c': { if (!have_field_width) this->append_format_output(fmt.c_str(), *argument); else this->append_format_output(fmt.c_str(), field_width, *argument); break; - case L's': - if (!have_field_width) - { - if (!have_precision) - { + } + case L's': { + if (!have_field_width) { + if (!have_precision) { this->append_format_output(fmt.c_str(), argument); - } - else + } else this->append_format_output(fmt.c_str(), precision, argument); - } - else - { + } else { if (!have_precision) this->append_format_output(fmt.c_str(), field_width, argument); else this->append_format_output(fmt.c_str(), field_width, precision, argument); } break; + } } } -/* For each character in str, set the corresponding boolean in the array to the given flag */ -static inline void modify_allowed_format_specifiers(bool ok[UCHAR_MAX + 1], const char *str, bool flag) -{ - for (const char *c = str; *c != '\0'; c++) - { +/// For each character in str, set the corresponding boolean in the array to the given flag. +static inline void modify_allowed_format_specifiers(bool ok[UCHAR_MAX + 1], const char *str, + bool flag) { + for (const char *c = str; *c != '\0'; c++) { unsigned char idx = static_cast(*c); ok[idx] = flag; } } -/* Print the text in FORMAT, using ARGV (with ARGC elements) for - arguments to any `%' directives. - Return the number of elements of ARGV used. */ +/// Print the text in FORMAT, using ARGV (with ARGC elements) for arguments to any `%' directives. +/// Return the number of elements of ARGV used. +int builtin_printf_state_t::print_formatted(const wchar_t *format, int argc, wchar_t **argv) { + int save_argc = argc; /* Preserve original value. */ + const wchar_t *f; /* Pointer into `format'. */ + const wchar_t *direc_start; /* Start of % directive. */ + size_t direc_length; /* Length of % directive. */ + bool have_field_width; /* True if FIELD_WIDTH is valid. */ + int field_width = 0; /* Arg to first '*'. */ + bool have_precision; /* True if PRECISION is valid. */ + int precision = 0; /* Arg to second '*'. */ + bool ok[UCHAR_MAX + 1] = {}; /* ok['x'] is true if %x is allowed. */ -int builtin_printf_state_t::print_formatted(const wchar_t *format, int argc, wchar_t **argv) -{ - int save_argc = argc; /* Preserve original value. */ - const wchar_t *f; /* Pointer into `format'. */ - const wchar_t *direc_start; /* Start of % directive. */ - size_t direc_length; /* Length of % directive. */ - bool have_field_width; /* True if FIELD_WIDTH is valid. */ - int field_width = 0; /* Arg to first '*'. */ - bool have_precision; /* True if PRECISION is valid. */ - int precision = 0; /* Arg to second '*'. */ - bool ok[UCHAR_MAX + 1] = { }; /* ok['x'] is true if %x is allowed. */ - - for (f = format; *f != L'\0'; ++f) - { - switch (*f) - { - case L'%': + for (f = format; *f != L'\0'; ++f) { + switch (*f) { + case L'%': { direc_start = f++; direc_length = 1; have_field_width = have_precision = false; - if (*f == L'%') - { + if (*f == L'%') { this->append_output(L'%'); break; } - if (*f == L'b') - { - /* FIXME: Field width and precision are not supported - for %b, even though POSIX requires it. */ - if (argc > 0) - { + if (*f == L'b') { + // FIXME: Field width and precision are not supported for %b, even though POSIX + // requires it. + if (argc > 0) { print_esc_string(*argv); ++argv; --argc; @@ -649,10 +593,8 @@ int builtin_printf_state_t::print_formatted(const wchar_t *format, int argc, wch modify_allowed_format_specifiers(ok, "aAcdeEfFgGiosuxX", true); - for (;; f++, direc_length++) - { - switch (*f) - { + for (;; f++, direc_length++) { + switch (*f) { case L'I': case L'\'': modify_allowed_format_specifiers(ok, "aAceEosxX", false); @@ -671,15 +613,12 @@ int builtin_printf_state_t::print_formatted(const wchar_t *format, int argc, wch goto no_more_flag_characters; } } -no_more_flag_characters: - ; + no_more_flag_characters:; - if (*f == L'*') - { + if (*f == L'*') { ++f; ++direc_length; - if (argc > 0) - { + if (argc > 0) { intmax_t width = string_to_scalar_type(*argv, this); if (INT_MIN <= width && width <= INT_MAX) field_width = static_cast(width); @@ -687,104 +626,83 @@ no_more_flag_characters: this->fatal_error(_(L"invalid field width: %ls"), *argv); ++argv; --argc; - } - else - { + } else { field_width = 0; } have_field_width = true; - } - else - { - while (iswdigit(*f)) - { + } else { + while (iswdigit(*f)) { ++f; ++direc_length; } } - if (*f == L'.') - { + if (*f == L'.') { ++f; ++direc_length; modify_allowed_format_specifiers(ok, "c", false); - if (*f == L'*') - { + if (*f == L'*') { ++f; ++direc_length; - if (argc > 0) - { + if (argc > 0) { intmax_t prec = string_to_scalar_type(*argv, this); - if (prec < 0) - { - /* A negative precision is taken as if the - precision were omitted, so -1 is safe - here even if prec < INT_MIN. */ + if (prec < 0) { + // A negative precision is taken as if the precision were omitted, + // so -1 is safe here even if prec < INT_MIN. precision = -1; - } - else if (INT_MAX < prec) + } else if (INT_MAX < prec) this->fatal_error(_(L"invalid precision: %ls"), *argv); - else - { + else { precision = static_cast(prec); } ++argv; --argc; - } - else - { + } else { precision = 0; } have_precision = true; - } - else - { - while (iswdigit(*f)) - { + } else { + while (iswdigit(*f)) { ++f; ++direc_length; } } } - while (*f == L'l' || *f == L'L' || *f == L'h' || *f == L'j' || *f == L't' || *f == L'z') + while (*f == L'l' || *f == L'L' || *f == L'h' || *f == L'j' || *f == L't' || + *f == L'z') { ++f; - - { - wchar_t conversion = *f; - if (conversion > 0xFF || ! ok[conversion]) - { - this->fatal_error(_(L"%.*ls: invalid conversion specification"), (int)(f + 1 - direc_start), direc_start); - return 0; - } } - print_direc(direc_start, direc_length, *f, - have_field_width, field_width, - have_precision, precision, - (argc <= 0 ? L"" : (argc--, *argv++))); - break; + wchar_t conversion = *f; + if (conversion > 0xFF || !ok[conversion]) { + this->fatal_error(_(L"%.*ls: invalid conversion specification"), + (int)(f + 1 - direc_start), direc_start); + return 0; + } - case L'\\': + print_direc(direc_start, direc_length, *f, have_field_width, field_width, + have_precision, precision, (argc <= 0 ? L"" : (argc--, *argv++))); + break; + } + case L'\\': { f += print_esc(f, false); break; - - default: - this->append_output(*f); + } + default: { this->append_output(*f); } } } return save_argc - argc; } -static int builtin_printf(parser_t &parser, io_streams_t &streams, wchar_t **argv) -{ +/// The printf builtin. +int builtin_printf(parser_t &parser, io_streams_t &streams, wchar_t **argv) { builtin_printf_state_t state(streams); wchar_t *format; int args_used; int argc = builtin_count_args(argv); - if (argc <= 1) - { + if (argc <= 1) { state.fatal_error(_(L"printf: not enough arguments")); return STATUS_BUILTIN_ERROR; } @@ -793,12 +711,10 @@ static int builtin_printf(parser_t &parser, io_streams_t &streams, wchar_t **arg argc -= 2; argv += 2; - do - { + do { args_used = state.print_formatted(format, argc, argv); argc -= args_used; argv += args_used; - } - while (args_used > 0 && argc > 0 && ! state.early_exit); + } while (args_used > 0 && argc > 0 && !state.early_exit); return state.exit_code; } diff --git a/src/builtin_printf.h b/src/builtin_printf.h new file mode 100644 index 000000000..f99a864fd --- /dev/null +++ b/src/builtin_printf.h @@ -0,0 +1,11 @@ +// Prototypes for functions for executing builtin_printf functions. +#ifndef FISH_BUILTIN_PRINTF_H +#define FISH_BUILTIN_PRINTF_H + +#include +#include + +class parser_t; + +int builtin_printf(parser_t &parser, io_streams_t &streams, wchar_t **argv); +#endif diff --git a/src/builtin_set.cpp b/src/builtin_set.cpp index 823bdebc7..2bc60d8b8 100644 --- a/src/builtin_set.cpp +++ b/src/builtin_set.cpp @@ -1,88 +1,72 @@ -/** \file builtin_set.c Functions defining the set builtin - -Functions used for implementing the set builtin. - -*/ +// Functions used for implementing the set builtin. +#include +#include #include +#include #include #include -#include #include -#include -#include -#include -#include -#include #include -#include +#include +#include +#include +#include -#include "fallback.h" // IWYU pragma: keep -#include "wutil.h" // IWYU pragma: keep #include "builtin.h" +#include "common.h" #include "env.h" #include "expand.h" -#include "common.h" -#include "wgetopt.h" -#include "proc.h" +#include "fallback.h" // IWYU pragma: keep #include "io.h" +#include "proc.h" +#include "wgetopt.h" +#include "wutil.h" // IWYU pragma: keep class parser_t; -/** - Error message for invalid path operations -*/ +// Error message for invalid path operations. #define BUILTIN_SET_PATH_ERROR L"%ls: Warning: path component %ls may not be valid in %ls.\n" -/** - Hint for invalid path operation with a colon -*/ +// Hint for invalid path operation with a colon. #define BUILTIN_SET_PATH_HINT L"%ls: Did you mean 'set %ls $%ls %ls'?\n" -/** - Error for mismatch between index count and elements -*/ -#define BUILTIN_SET_ARG_COUNT L"%ls: The number of variable indexes does not match the number of values\n" +// Error for mismatch between index count and elements. +#define BUILTIN_SET_ARG_COUNT \ + L"%ls: The number of variable indexes does not match the number of values\n" -/** - Test if the specified variable should be subject to path validation -*/ -static int is_path_variable(const wchar_t *env) -{ - return contains(env, L"PATH", L"CDPATH"); -} +// Test if the specified variable should be subject to path validation. +static int is_path_variable(const wchar_t *env) { 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, io_streams_t &streams) -{ +/// 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, + io_streams_t &streams) { size_t i; int retcode = 0; - const wchar_t *val_str=NULL; + const wchar_t *val_str = NULL; - if (is_path_variable(key)) - { - /* Fix for https://github.com/fish-shell/fish-shell/issues/199 . Return success if any path setting succeeds. */ + 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; - /* Don't bother validating (or complaining about) values that are already present. - When determining already-present values, use ENV_DEFAULT instead of the passed-in scope because in: - set -l PATH stuff $PATH - where we are temporarily shadowing a variable, we want to compare against the shadowed value, not the - (missing) local value. - Also don't bother to complain about relative paths, which don't start with /. - */ + // Don't bother validating (or complaining about) values that are already present. When + // determining already-present values, use ENV_DEFAULT instead of the passed-in scope + // because in: + // + // set -l PATH stuff $PATH + // + // where we are temporarily shadowing a variable, we want to compare against the shadowed + // value, not the (missing) local value. Also don't bother to complain about relative paths, + // which don't start with /. wcstring_list_t existing_values; const env_var_t existing_variable = env_get_string(key, ENV_DEFAULT); - if (! existing_variable.missing_or_empty()) + if (!existing_variable.missing_or_empty()) tokenize_variable_array(existing_variable, existing_values); - for (i=0; i< val.size() ; i++) - { + for (i = 0; i < val.size(); i++) { const wcstring &dir = val.at(i); - if (!string_prefixes_string(L"/", dir) || list_contains_string(existing_values, dir)) - { + if (!string_prefixes_string(L"/", dir) || list_contains_string(existing_values, dir)) { any_success = true; continue; } @@ -92,87 +76,73 @@ static int my_env_set(const wchar_t *key, const wcstring_list_t &val, int scope, bool error = false; struct stat buff; - 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; - } - else - { + } else { streams.err.append_format(_(BUILTIN_SET_PATH_ERROR), L"set", dir.c_str(), key); const wchar_t *colon = wcschr(dir.c_str(), L':'); - if (colon && *(colon+1)) - { + if (colon && *(colon + 1)) { show_hint = 1; } - } - if (show_perror) - { + if (show_perror) { builtin_wperror(L"set", streams); } - if (show_hint) - { - streams.err.append_format(_(BUILTIN_SET_PATH_HINT), L"set", key, key, wcschr(dir.c_str(), L':')+1); + if (show_hint) { + streams.err.append_format(_(BUILTIN_SET_PATH_HINT), L"set", key, key, + wcschr(dir.c_str(), 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) - { + // Fail at setting the path if we tried to set it to something non-empty, but it wound up + // empty. + if (!val.empty() && !any_success) { return 1; } - } wcstring sb; - if (! val.empty()) - { - for (i=0; i< val.size() ; i++) - { + if (!val.empty()) { + 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, - io_streams_t &streams) -{ +/// Extract indexes from a destination argument of the form name[index1 index2...] +/// +/// \param indexes the list to insert the new indexes into +/// \param src the source string to parse +/// \param name the name of the element. Return null if the name in \c src does not match this name +/// \param var_count the number of elements in the array to parse. +/// +/// \return the total number of indexes parsed, or -1 on error +static int parse_index(std::vector &indexes, const wchar_t *src, const wchar_t *name, + size_t var_count, io_streams_t &streams) { size_t len; int count = 0; const wchar_t *src_orig = src; - if (src == 0) - { + if (src == 0) { return 0; } - while (*src != L'\0' && (iswalnum(*src) || *src == L'_')) - { + while (*src != L'\0' && (iswalnum(*src) || *src == L'_')) { src++; } - if (*src != L'[') - { + if (*src != L'[') { streams.err.append_format(_(BUILTIN_SET_ARG_COUNT), L"set"); return 0; } - len = src-src_orig; + len = src - src_orig; - if ((wcsncmp(src_orig, name, len)!=0) || (wcslen(name) != (len))) - { - streams.err.append_format(_(L"%ls: Multiple variable names specified in single call (%ls and %.*ls)\n"), - L"set", - name, - len, - src_orig); + if ((wcsncmp(src_orig, name, len) != 0) || (wcslen(name) != (len))) { + streams.err.append_format( + _(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)) - { + while (iswspace(*src)) { src++; } - while (*src != L']') - { + while (*src != L']') { wchar_t *end; long l_ind; @@ -248,42 +202,34 @@ static int parse_index(std::vector &indexes, l_ind = wcstol(src, &end, 10); - if (end==src || errno) - { + if (end == src || errno) { streams.err.append_format(_(L"%ls: Invalid index starting at '%ls'\n"), L"set", src); return 0; } - if (l_ind < 0) - { - l_ind = var_count+l_ind+1; + if (l_ind < 0) { + l_ind = var_count + l_ind + 1; } src = end; - if (*src==L'.' && *(src+1)==L'.') - { - src+=2; + if (*src == L'.' && *(src + 1) == L'.') { + src += 2; long l_ind2 = wcstol(src, &end, 10); - if (end==src || errno) - { + if (end == src || errno) { return 1; } src = end; - if (l_ind2 < 0) - { - l_ind2 = var_count+l_ind2+1; + if (l_ind2 < 0) { + l_ind2 = var_count + l_ind2 + 1; } - int direction = l_ind2 &indexes, return count; } -static int update_values(wcstring_list_t &list, - std::vector &indexes, - wcstring_list_t &values) -{ +static int update_values(wcstring_list_t &list, std::vector &indexes, + wcstring_list_t &values) { 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 - */ + // 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) - { + const wcstring newv = values[i]; + if (ind < 0) { return 1; } - if ((size_t)ind >= list.size()) - { - list.resize(ind+1); + if ((size_t)ind >= list.size()) { + list.resize(ind + 1); } -// free((void *) al_get(list, ind)); - list[ ind ] = newv; + // free((void *) al_get(list, ind)); + list[ind] = newv; } return 0; } -/** - Erase from a list of wcstring values at specified indexes -*/ -static void erase_values(wcstring_list_t &list, const std::vector &indexes) -{ +/// Erase from a list of wcstring values at specified indexes. +static void erase_values(wcstring_list_t &list, const std::vector &indexes) { // Make a set of indexes. // This both sorts them into ascending order and removes duplicates. const std::set indexes_set(indexes.begin(), indexes.end()); - // Now walk the set backwards, so we encounter larger indexes first, and remove elements at the given (1-based) indexes. + // 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); } } } - -/** - Print the names of all environment variables in the scope, with or without shortening, - with or without values, with or without escaping -*/ -static void print_variables(int include_values, int esc, bool shorten_ok, int scope, io_streams_t &streams) -{ +/// Print the names of all environment variables in the scope, with or without shortening, with or +/// without values, with or without escaping +static void print_variables(int include_values, int esc, bool shorten_ok, int scope, + io_streams_t &streams) { wcstring_list_t names = env_get_names(scope); sort(names.begin(), names.end()); - for (size_t i = 0; i < names.size(); i++) - { + for (size_t i = 0; i < names.size(); i++) { const wcstring key = names.at(i); const wcstring e_key = escape_string(key, 0); streams.out.append(e_key); - if (include_values) - { + if (include_values) { env_var_t value = env_get_string(key, scope); - if (!value.missing()) - { + if (!value.missing()) { int shorten = 0; - if (shorten_ok && value.length() > 64) - { + if (shorten_ok && value.length() > 64) { shorten = 1; value.resize(60); } @@ -381,11 +309,9 @@ static void print_variables(int include_values, int esc, bool shorten_ok, int sc streams.out.append(L" "); streams.out.append(e_value); - if (shorten) - { + if (shorten) { streams.out.push_back(ellipsis_char); } - } } @@ -393,117 +319,101 @@ 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, io_streams_t &streams, wchar_t **argv) -{ +/// The set builtin. Creates, updates and erases environment variables and environemnt variable +/// arrays. +int builtin_set(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wgetopter_t w; - /** Variables used for parsing the argument list */ - 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 } - } ; + // Variables used for parsing the argument list. + 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 - */ + // 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; + int erase = 0, list = 0, unexport = 0; + int universal = 0, query = 0; bool shorten_ok = true; bool preserve_incoming_failure_exit_status = true; const int incoming_exit_status = proc_get_last_status(); - /* - Variables used for performing the actual work - */ + // Variables used for performing the actual work. wchar_t *dest = 0; - int retcode=0; + int retcode = 0; int scope; - int slice=0; + int slice = 0; const wchar_t *bad_char = NULL; - - /* Parse options to obtain the requested operation and the modifiers */ + // Parse options to obtain the requested operation and the modifiers. w.woptind = 0; - while (1) - { + while (1) { int c = w.wgetopt_long(argc, argv, short_options, long_options, 0); - if (c == -1) - { + if (c == -1) { break; } - switch (c) - { - case 0: + switch (c) { + case 0: { break; - - case 'e': + } + case 'e': { erase = 1; preserve_incoming_failure_exit_status = false; break; - - case 'n': + } + case 'n': { list = 1; preserve_incoming_failure_exit_status = false; break; - - case 'x': + } + case 'x': { exportv = 1; break; - - case 'l': + } + case 'l': { local = 1; break; - - case 'g': + } + case 'g': { global = 1; break; - - case 'u': + } + case 'u': { unexport = 1; break; - - case 'U': + } + case 'U': { universal = 1; break; - - case 'L': + } + case 'L': { shorten_ok = false; break; - - case 'q': + } + case 'q': { query = 1; preserve_incoming_failure_exit_status = false; break; - - case 'h': + } + case 'h': { builtin_print_help(parser, streams, argv[0], streams.out); return 0; - + } case '?': - builtin_unknown_option(parser, streams, argv[0], argv[w.woptind-1]); + builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]); return 1; default: @@ -511,229 +421,164 @@ static int builtin_set(parser_t &parser, io_streams_t &streams, wchar_t **argv) } } - /* - 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)) - { - streams.err.append_format(BUILTIN_ERR_COMBO, - argv[0]); + // 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)) { + streams.err.append_format(BUILTIN_ERR_COMBO, argv[0]); builtin_print_help(parser, streams, argv[0], streams.err); return 1; } - - /* We can't both list and erase variables */ - if (erase && list) - { - streams.err.append_format(BUILTIN_ERR_COMBO, - argv[0]); + // We can't both list and erase variables. + if (erase && list) { + streams.err.append_format(BUILTIN_ERR_COMBO, argv[0]); builtin_print_help(parser, streams, argv[0], streams.err); return 1; } - /* - Variables can only have one scope - */ - if (local + global + universal > 1) - { - streams.err.append_format(BUILTIN_ERR_GLOCAL, - argv[0]); + // Variables can only have one scope. + if (local + global + universal > 1) { + streams.err.append_format(BUILTIN_ERR_GLOCAL, argv[0]); builtin_print_help(parser, streams, argv[0], streams.err); return 1; } - /* - Variables can only have one export status - */ - if (exportv && unexport) - { - streams.err.append_format(BUILTIN_ERR_EXPUNEXP, - argv[0]); + // Variables can only have one export status. + if (exportv && unexport) { + streams.err.append_format(BUILTIN_ERR_EXPUNEXP, argv[0]); builtin_print_help(parser, streams, argv[0], streams.err); 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; + // 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. - */ + if (query) { + // Query mode. Return the number of variables that do not exist out of the specified + // variables. int i; - for (i=w.woptind; i indexes; wcstring_list_t result; size_t j; env_var_t dest_str = env_get_string(dest, scope); - if (! dest_str.missing()) - tokenize_variable_array(dest_str, result); + if (!dest_str.missing()) tokenize_variable_array(dest_str, result); - if (!parse_index(indexes, arg, dest, result.size(), streams)) - { + if (!parse_index(indexes, arg, dest, result.size(), streams)) { builtin_print_help(parser, streams, argv[0], streams.err); retcode = 1; break; } - for (j=0; j < indexes.size() ; j++) - { + for (j = 0; j < indexes.size(); j++) { long idx = indexes[j]; - if (idx < 1 || (size_t)idx > result.size()) - { + if (idx < 1 || (size_t)idx > result.size()) { retcode++; } } - } - else - { - if (!env_exist(arg, scope)) - { + } else { + if (!env_exist(arg, scope)) { retcode++; } } free(dest); - } return retcode; } - if (list) - { - /* Maybe we should issue an error if there are any other arguments? */ + if (list) { + // Maybe we should issue an error if there are any other arguments? print_variables(0, 0, shorten_ok, scope, streams); return 0; } - if (w.woptind == argc) - { - /* - Print values of variables - */ - - if (erase) - { - streams.err.append_format(_(L"%ls: Erase needs a variable name\n"), - argv[0]); + if (w.woptind == argc) { + // Print values of variables. + if (erase) { + streams.err.append_format(_(L"%ls: Erase needs a variable name\n"), argv[0]); builtin_print_help(parser, streams, argv[0], streams.err); retcode = 1; - } - else - { + } else { print_variables(1, 1, shorten_ok, scope, streams); } return retcode; } - if (!(dest = wcsdup(argv[w.woptind]))) - { + if (!(dest = wcsdup(argv[w.woptind]))) { DIE_MEM(); } - if (wcschr(dest, L'[')) - { + if (wcschr(dest, L'[')) { slice = 1; - *wcschr(dest, L'[')=0; + *wcschr(dest, L'[') = 0; } - if (!wcslen(dest)) - { + if (!wcslen(dest)) { free(dest); streams.err.append_format(BUILTIN_ERR_VARNAME_ZERO, argv[0]); builtin_print_help(parser, streams, argv[0], streams.err); return 1; } - if ((bad_char = wcsvarname(dest))) - { + if ((bad_char = wcsvarname(dest))) { streams.err.append_format(BUILTIN_ERR_VARCHAR, argv[0], *bad_char); builtin_print_help(parser, streams, argv[0], streams.err); free(dest); return 1; } - /* - set assignment can work in two modes, either using slices or - using the whole array. We detect which mode is used here. - */ - - if (slice) - { - - /* - Slice mode - */ + // Set assignment can work in two modes, either using slices or using the whole array. We detect + // which mode is used here. + if (slice) { + // Slice mode. std::vector indexes; wcstring_list_t result; const env_var_t dest_str = env_get_string(dest, scope); - if (! dest_str.missing()) - { + if (!dest_str.missing()) { tokenize_variable_array(dest_str, result); - } - else if (erase) - { + } else if (erase) { retcode = 1; } - if (!retcode) - { - for (; w.woptind +#include + +class parser_t; + +int builtin_set(parser_t &parser, io_streams_t &streams, wchar_t **argv); +#endif diff --git a/src/builtin_set_color.cpp b/src/builtin_set_color.cpp index d3b744cd9..2cd1d876f 100644 --- a/src/builtin_set_color.cpp +++ b/src/builtin_set_color.cpp @@ -1,8 +1,4 @@ -/** \file builtin_set_color.cpp Functions defining the set_color builtin - -Functions used for implementing the set_color builtin. - -*/ +// Functions used for implementing the set_color builtin. #include "config.h" #if HAVE_NCURSES_H @@ -17,234 +13,197 @@ Functions used for implementing the set_color builtin. #elif HAVE_NCURSES_TERM_H #include #endif -#include #include +#include +#include +#include +#include #include #include -#include -#include -#include #include "builtin.h" #include "color.h" -#include "output.h" -#include "wgetopt.h" -#include "proc.h" -#include "io.h" #include "common.h" +#include "io.h" +#include "output.h" +#include "proc.h" +#include "wgetopt.h" #include "wutil.h" // IWYU pragma: keep class parser_t; -/** - Error message for invalid path operations -*/ +/// Error message for invalid path operations. #define BUILTIN_SET_PATH_ERROR L"%ls: Warning: path component %ls may not be valid in %ls.\n" -/** - Hint for invalid path operation with a colon -*/ +/// Hint for invalid path operation with a colon. #define BUILTIN_SET_PATH_HINT L"%ls: Did you mean 'set %ls $%ls %ls'?\n" -/** - Error for mismatch between index count and elements -*/ -#define BUILTIN_SET_ARG_COUNT L"%ls: The number of variable indexes does not match the number of values\n" +/// Error for mismatch between index count and elements. +#define BUILTIN_SET_ARG_COUNT \ + L"%ls: The number of variable indexes does not match the number of values\n" -static void print_colors(io_streams_t &streams) -{ +static void print_colors(io_streams_t &streams) { const wcstring_list_t result = rgb_color_t::named_color_names(); size_t i; - for (i=0; i < result.size(); i++) - { + for (i = 0; i < result.size(); i++) { streams.out.append(result.at(i)); streams.out.push_back(L'\n'); } } -/* function we set as the output writer */ static std::string builtin_set_color_output; -static int set_color_builtin_outputter(char c) -{ +/// Function we set as the output writer. +static int set_color_builtin_outputter(char c) { ASSERT_IS_MAIN_THREAD(); builtin_set_color_output.push_back(c); return 0; } -/** - set_color builtin -*/ -static int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t **argv) -{ +/// set_color builtin. +int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wgetopter_t w; - /** Variables used for parsing the argument list */ - const struct woption long_options[] = - { - { L"background", required_argument, 0, 'b'}, - { L"help", no_argument, 0, 'h' }, - { L"bold", no_argument, 0, 'o' }, - { L"underline", no_argument, 0, 'u' }, - { L"version", no_argument, 0, 'v' }, - { L"print-colors", no_argument, 0, 'c' }, - { 0, 0, 0, 0 } - }; + // Variables used for parsing the argument list. + const struct woption long_options[] = {{L"background", required_argument, 0, 'b'}, + {L"help", no_argument, 0, 'h'}, + {L"bold", no_argument, 0, 'o'}, + {L"underline", no_argument, 0, 'u'}, + {L"version", no_argument, 0, 'v'}, + {L"print-colors", no_argument, 0, 'c'}, + {0, 0, 0, 0}}; const wchar_t *short_options = L"b:hvocu"; int argc = builtin_count_args(argv); - /* Some code passes variables to set_color that don't exist, like $fish_user_whatever. As a hack, quietly return failure. */ - if (argc <= 1) - { + // Some code passes variables to set_color that don't exist, like $fish_user_whatever. As a + // hack, quietly return failure. + if (argc <= 1) { return EXIT_FAILURE; } const wchar_t *bgcolor = NULL; - bool bold = false, underline=false; + bool bold = false, underline = false; int errret; - /* Parse options to obtain the requested operation and the modifiers */ + // Parse options to obtain the requested operation and the modifiers. w.woptind = 0; - while (1) - { + while (1) { int c = w.wgetopt_long(argc, argv, short_options, long_options, 0); - if (c == -1) - { + if (c == -1) { break; } - switch (c) - { - case 0: + switch (c) { + case 0: { break; - - case 'b': + } + case 'b': { bgcolor = w.woptarg; break; - - case 'h': + } + case 'h': { builtin_print_help(parser, streams, argv[0], streams.out); return STATUS_BUILTIN_OK; - - case 'o': + } + case 'o': { bold = true; break; - - case 'u': + } + case 'u': { underline = true; break; - - case 'c': + } + case 'c': { print_colors(streams); return STATUS_BUILTIN_OK; - - case '?': + } + case '?': { return STATUS_BUILTIN_ERROR; + } } } - /* Remaining arguments are foreground color */ + // Remaining arguments are foreground color. std::vector fgcolors; - for (; w.woptind < argc; w.woptind++) - { + for (; w.woptind < argc; w.woptind++) { rgb_color_t fg = rgb_color_t(argv[w.woptind]); - if (fg.is_none()) - { + if (fg.is_none()) { streams.err.append_format(_(L"%ls: Unknown color '%ls'\n"), argv[0], argv[w.woptind]); return STATUS_BUILTIN_ERROR; } fgcolors.push_back(fg); } - if (fgcolors.empty() && bgcolor == NULL && !bold && !underline) - { - streams.err.append_format(_(L"%ls: Expected an argument\n"), - argv[0]); + if (fgcolors.empty() && bgcolor == NULL && !bold && !underline) { + streams.err.append_format(_(L"%ls: Expected an argument\n"), argv[0]); return STATUS_BUILTIN_ERROR; } - - // #1323: We may have multiple foreground colors. Choose the best one. - // If we had no foreground coor, we'll get none(); if we have at least one we expect not-none + + // #1323: We may have multiple foreground colors. Choose the best one. If we had no foreground + // color, we'll get none(); if we have at least one we expect not-none. const rgb_color_t fg = best_color(fgcolors, output_get_color_support()); assert(fgcolors.empty() || !fg.is_none()); const rgb_color_t bg = rgb_color_t(bgcolor ? bgcolor : L""); - if (bgcolor && bg.is_none()) - { + if (bgcolor && bg.is_none()) { streams.err.append_format(_(L"%ls: Unknown color '%ls'\n"), argv[0], bgcolor); return STATUS_BUILTIN_ERROR; } - /* Make sure that the term exists */ - if (cur_term == NULL && setupterm(0, STDOUT_FILENO, &errret) == ERR) - { + // Make sure that the term exists. + if (cur_term == NULL && setupterm(0, STDOUT_FILENO, &errret) == ERR) { streams.err.append_format(_(L"%ls: Could not set up terminal\n"), argv[0]); return STATUS_BUILTIN_ERROR; } - /* - Test if we have at least basic support for setting fonts, colors - and related bits - otherwise just give up... - */ - if (! exit_attribute_mode) - { + // Test if we have at least basic support for setting fonts, colors and related bits - otherwise + // just give up... + if (!exit_attribute_mode) { return STATUS_BUILTIN_ERROR; } - /* Save old output function so we can restore it */ - int (* const saved_writer_func)(char) = output_get_writer(); + // Save old output function so we can restore it. + int (*const saved_writer_func)(char) = output_get_writer(); - /* Set our output function, which writes to a std::string */ + // Set our output function, which writes to a std::string. builtin_set_color_output.clear(); output_set_writer(set_color_builtin_outputter); - if (bold) - { - if (enter_bold_mode) - writembs(tparm(enter_bold_mode)); + if (bold) { + if (enter_bold_mode) writembs(tparm(enter_bold_mode)); } - if (underline) - { - if (enter_underline_mode) - writembs(enter_underline_mode); + if (underline) { + if (enter_underline_mode) writembs(enter_underline_mode); } - if (bgcolor != NULL) - { - if (bg.is_normal()) - { + if (bgcolor != NULL) { + if (bg.is_normal()) { write_color(rgb_color_t::black(), false /* not is_fg */); writembs(tparm(exit_attribute_mode)); } } - if (! fg.is_none()) - { - if (fg.is_normal() || fg.is_reset()) - { + if (!fg.is_none()) { + if (fg.is_normal() || fg.is_reset()) { write_color(rgb_color_t::black(), true /* is_fg */); writembs(tparm(exit_attribute_mode)); - } - else - { + } else { write_color(fg, true /* is_fg */); } } - if (bgcolor != NULL) - { - if (! bg.is_normal() && ! bg.is_reset()) - { + if (bgcolor != NULL) { + if (!bg.is_normal() && !bg.is_reset()) { write_color(bg, false /* not is_fg */); } } - /* Restore saved writer function */ + // Restore saved writer function. output_set_writer(saved_writer_func); - /* Output the collected string */ + // Output the collected string. streams.out.append(str2wcstring(builtin_set_color_output)); builtin_set_color_output.clear(); diff --git a/src/builtin_set_color.h b/src/builtin_set_color.h new file mode 100644 index 000000000..7e41cd58b --- /dev/null +++ b/src/builtin_set_color.h @@ -0,0 +1,11 @@ +// Prototypes for functions for executing builtin_set_color functions. +#ifndef FISH_BUILTIN_SET_COLOR_H +#define FISH_BUILTIN_SET_COLOR_H + +#include +#include + +class parser_t; + +int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t **argv); +#endif diff --git a/src/builtin_string.cpp b/src/builtin_string.cpp index 51c824ca8..9e6c11a42 100644 --- a/src/builtin_string.cpp +++ b/src/builtin_string.cpp @@ -1,54 +1,41 @@ -/** \file builtin_string.cpp - Implementation of the string builtin. -*/ +// Implementation of the string builtin. #include "config.h" #define PCRE2_CODE_UNIT_WIDTH WCHAR_T_BITS #ifdef _WIN32 #define PCRE2_STATIC #endif -#include -#include #include +#include #include #include +#include #include -#include +#include #include #include +#include +#include #include #include -#include -#include -#include "pcre2.h" #include "builtin.h" #include "common.h" -#include "parse_util.h" -#include "wgetopt.h" -#include "wildcard.h" #include "fallback.h" // IWYU pragma: keep #include "io.h" +#include "parse_util.h" +#include "pcre2.h" +#include "wgetopt.h" +#include "wildcard.h" #include "wutil.h" // IWYU pragma: keep class parser_t; -#define STRING_ERR_MISSING _(L"%ls: Expected argument\n") +#define STRING_ERR_MISSING _(L"%ls: Expected argument\n") -/* externs from builtin.cpp */ -extern int builtin_count_args(const wchar_t * const * argv); -void builtin_print_help(parser_t &parser, io_streams_t &streams, const wchar_t *cmd, output_stream_t &b); +enum { BUILTIN_STRING_OK = 0, BUILTIN_STRING_NONE = 1, BUILTIN_STRING_ERROR = 2 }; - -enum -{ - BUILTIN_STRING_OK = 0, - BUILTIN_STRING_NONE = 1, - BUILTIN_STRING_ERROR = 2 -}; - -static void string_error(io_streams_t &streams, const wchar_t *fmt, ...) -{ +static void string_error(io_streams_t &streams, const wchar_t *fmt, ...) { streams.err.append(L"string "); va_list va; va_start(va, fmt); @@ -56,111 +43,88 @@ static void string_error(io_streams_t &streams, const wchar_t *fmt, ...) va_end(va); } -static void string_unknown_option(parser_t &parser, io_streams_t &streams, const wchar_t *subcmd, const wchar_t *opt) -{ +static void string_unknown_option(parser_t &parser, io_streams_t &streams, const wchar_t *subcmd, + const wchar_t *opt) { string_error(streams, BUILTIN_ERR_UNKNOWN, subcmd, opt); builtin_print_help(parser, streams, L"string", streams.err); } -/* We read from stdin if we are the second or later process in a pipeline. */ -static bool string_args_from_stdin(const io_streams_t &streams) -{ +// We read from stdin if we are the second or later process in a pipeline. +static bool string_args_from_stdin(const io_streams_t &streams) { return streams.stdin_is_directly_redirected; } -static const wchar_t *string_get_arg_stdin(wcstring *storage, const io_streams_t &streams) -{ +static const wchar_t *string_get_arg_stdin(wcstring *storage, const io_streams_t &streams) { std::string arg; - for (;;) - { + for (;;) { char ch = '\0'; long rc = read_blocked(streams.stdin_fd, &ch, 1); - if (rc < 0) - { - // failure + if (rc < 0) { // failure return 0; } - if (rc == 0) - { - // eof - if (arg.empty()) - { + if (rc == 0) { // EOF + if (arg.empty()) { return 0; - } - else - { + } else { break; } } - if (ch == '\n') - { + if (ch == '\n') { break; } arg += ch; } - + *storage = str2wcstring(arg); return storage->c_str(); } -static const wchar_t *string_get_arg_argv(int *argidx, wchar_t **argv) -{ +static const wchar_t *string_get_arg_argv(int *argidx, wchar_t **argv) { return (argv && argv[*argidx]) ? argv[(*argidx)++] : 0; } -static const wchar_t *string_get_arg(int *argidx, wchar_t **argv, wcstring *storage, const io_streams_t &streams) -{ - if (string_args_from_stdin(streams)) - { +static const wchar_t *string_get_arg(int *argidx, wchar_t **argv, wcstring *storage, + const io_streams_t &streams) { + if (string_args_from_stdin(streams)) { return string_get_arg_stdin(storage, streams); - } - else - { + } else { return string_get_arg_argv(argidx, argv); } } -static int string_escape(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv) -{ +static int string_escape(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv) { const wchar_t *short_options = L"n"; - const struct woption long_options[] = - { - { L"no-quoted", no_argument, 0, 'n' }, - { 0, 0, 0, 0 } - }; + const struct woption long_options[] = {{L"no-quoted", no_argument, 0, 'n'}, {0, 0, 0, 0}}; escape_flags_t flags = ESCAPE_ALL; wgetopter_t w; - for (;;) - { + for (;;) { int c = w.wgetopt_long(argc, argv, short_options, long_options, 0); - if (c == -1) - { + if (c == -1) { break; } - switch (c) - { - case 0: + switch (c) { + case 0: { break; - - case 'n': + } + case 'n': { flags |= ESCAPE_NO_QUOTED; break; - - case '?': + } + case '?': { string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]); return BUILTIN_STRING_ERROR; + } } } int i = w.woptind; - if (string_args_from_stdin(streams) && argc > i) - { + if (string_args_from_stdin(streams) && argc > i) { string_error(streams, BUILTIN_ERR_TOO_MANY_ARGUMENTS, argv[0]); return BUILTIN_STRING_ERROR; } @@ -168,8 +132,7 @@ static int string_escape(parser_t &parser, io_streams_t &streams, int argc, wcha int nesc = 0; wcstring storage; const wchar_t *arg; - while ((arg = string_get_arg(&i, argv, &storage, streams)) != 0) - { + while ((arg = string_get_arg(&i, argv, &storage, streams)) != 0) { streams.out.append(escape(arg, flags)); streams.out.append(L'\n'); nesc++; @@ -178,50 +141,40 @@ static int string_escape(parser_t &parser, io_streams_t &streams, int argc, wcha return (nesc > 0) ? BUILTIN_STRING_OK : BUILTIN_STRING_NONE; } -static int string_join(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv) -{ +static int string_join(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv) { const wchar_t *short_options = L"q"; - const struct woption long_options[] = - { - { L"quiet", no_argument, 0, 'q'}, - { 0, 0, 0, 0 } - }; + const struct woption long_options[] = {{L"quiet", no_argument, 0, 'q'}, {0, 0, 0, 0}}; bool quiet = false; wgetopter_t w; - for (;;) - { + for (;;) { int c = w.wgetopt_long(argc, argv, short_options, long_options, 0); - - if (c == -1) - { + if (c == -1) { break; } - switch (c) - { - case 0: + switch (c) { + case 0: { break; - - case 'q': + } + case 'q': { quiet = true; break; - - case '?': + } + case '?': { string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]); return BUILTIN_STRING_ERROR; + } } } int i = w.woptind; const wchar_t *sep; - if ((sep = string_get_arg_argv(&i, argv)) == 0) - { + if ((sep = string_get_arg_argv(&i, argv)) == 0) { string_error(streams, STRING_ERR_MISSING, argv[0]); return BUILTIN_STRING_ERROR; } - if (string_args_from_stdin(streams) && argc > i) - { + if (string_args_from_stdin(streams) && argc > i) { string_error(streams, BUILTIN_ERR_TOO_MANY_ARGUMENTS, argv[0]); return BUILTIN_STRING_ERROR; } @@ -229,63 +182,51 @@ static int string_join(parser_t &parser, io_streams_t &streams, int argc, wchar_ int nargs = 0; const wchar_t *arg; wcstring storage; - while ((arg = string_get_arg(&i, argv, &storage, streams)) != 0) - { - if (!quiet) - { - if (nargs > 0) - { + while ((arg = string_get_arg(&i, argv, &storage, streams)) != 0) { + if (!quiet) { + if (nargs > 0) { streams.out.append(sep); } streams.out.append(arg); } nargs++; } - if (nargs > 0 && !quiet) - { + if (nargs > 0 && !quiet) { streams.out.push_back(L'\n'); } return (nargs > 1) ? BUILTIN_STRING_OK : BUILTIN_STRING_NONE; } -static int string_length(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv) -{ +static int string_length(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv) { const wchar_t *short_options = L"q"; - const struct woption long_options[] = - { - { L"quiet", no_argument, 0, 'q'}, - { 0, 0, 0, 0 } - }; + const struct woption long_options[] = {{L"quiet", no_argument, 0, 'q'}, {0, 0, 0, 0}}; bool quiet = false; wgetopter_t w; - for (;;) - { + for (;;) { int c = w.wgetopt_long(argc, argv, short_options, long_options, 0); - if (c == -1) - { + if (c == -1) { break; } - switch (c) - { - case 0: + switch (c) { + case 0: { break; - - case 'q': + } + case 'q': { quiet = true; break; - - case '?': + } + case '?': { string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]); return BUILTIN_STRING_ERROR; + } } } int i = w.woptind; - if (string_args_from_stdin(streams) && argc > i) - { + if (string_args_from_stdin(streams) && argc > i) { string_error(streams, BUILTIN_ERR_TOO_MANY_ARGUMENTS, argv[0]); return BUILTIN_STRING_ERROR; } @@ -293,15 +234,12 @@ static int string_length(parser_t &parser, io_streams_t &streams, int argc, wcha const wchar_t *arg; int nnonempty = 0; wcstring storage; - while ((arg = string_get_arg(&i, argv, &storage, streams)) != 0) - { + while ((arg = string_get_arg(&i, argv, &storage, streams)) != 0) { size_t n = wcslen(arg); - if (n > 0) - { + if (n > 0) { nnonempty++; } - if (!quiet) - { + if (!quiet) { streams.out.append(to_string(n)); streams.out.append(L'\n'); } @@ -310,84 +248,70 @@ static int string_length(parser_t &parser, io_streams_t &streams, int argc, wcha return (nnonempty > 0) ? BUILTIN_STRING_OK : BUILTIN_STRING_NONE; } -struct match_options_t -{ +struct match_options_t { bool all; bool ignore_case; bool index; bool invert_match; bool quiet; - match_options_t(): all(false), ignore_case(false), index(false), invert_match(false), quiet(false) { } + match_options_t() + : all(false), ignore_case(false), index(false), invert_match(false), quiet(false) {} }; -class string_matcher_t -{ -protected: +class string_matcher_t { + protected: match_options_t opts; io_streams_t &streams; int total_matched; -public: + public: string_matcher_t(const match_options_t &opts_, io_streams_t &streams_) - : opts(opts_), streams(streams_), total_matched(0) - { } + : opts(opts_), streams(streams_), total_matched(0) {} - virtual ~string_matcher_t() { } + virtual ~string_matcher_t() {} virtual bool report_matches(const wchar_t *arg) = 0; int match_count() { return total_matched; } }; -class wildcard_matcher_t: public string_matcher_t -{ -private: +class wildcard_matcher_t : public string_matcher_t { + private: wcstring wcpattern; -public: - wildcard_matcher_t(const wchar_t * /*argv0*/, const wchar_t *pattern, const match_options_t &opts, io_streams_t &streams) - : string_matcher_t(opts, streams), wcpattern(parse_util_unescape_wildcards(pattern)) - { - if (opts.ignore_case) - { - for (size_t i = 0; i < wcpattern.length(); i++) - { + + public: + wildcard_matcher_t(const wchar_t * /*argv0*/, const wchar_t *pattern, + const match_options_t &opts, io_streams_t &streams) + : string_matcher_t(opts, streams), wcpattern(parse_util_unescape_wildcards(pattern)) { + if (opts.ignore_case) { + for (size_t i = 0; i < wcpattern.length(); i++) { wcpattern[i] = towlower(wcpattern[i]); } } } - virtual ~wildcard_matcher_t() { } + virtual ~wildcard_matcher_t() {} - bool report_matches(const wchar_t *arg) - { - // Note: --all is a no-op for glob matching since the pattern is always - // matched against the entire argument + bool report_matches(const wchar_t *arg) { + // Note: --all is a no-op for glob matching since the pattern is always matched against the + // entire argument. bool match; - if (opts.ignore_case) - { + if (opts.ignore_case) { wcstring s = arg; - for (size_t i = 0; i < s.length(); i++) - { + for (size_t i = 0; i < s.length(); i++) { s[i] = towlower(s[i]); } match = wildcard_match(s, wcpattern, false); - } - else - { + } else { match = wildcard_match(arg, wcpattern, false); } - if (match ^ opts.invert_match) - { + if (match ^ opts.invert_match) { total_matched++; - if (!opts.quiet) - { - if (opts.index) - { + if (!opts.quiet) { + if (opts.index) { streams.out.append_format(L"1 %lu\n", wcslen(arg)); - } - else - { + } else { streams.out.append(arg); streams.out.append(L'\n'); } @@ -397,22 +321,20 @@ public: } }; -static wcstring pcre2_strerror(int err_code) -{ +static wcstring pcre2_strerror(int err_code) { wchar_t buf[128]; pcre2_get_error_message(err_code, (PCRE2_UCHAR *)buf, sizeof(buf) / sizeof(wchar_t)); return buf; } -struct compiled_regex_t -{ +struct compiled_regex_t { pcre2_code *code; pcre2_match_data *match; - compiled_regex_t(const wchar_t *argv0, const wchar_t *pattern, bool ignore_case, io_streams_t &streams) - : code(0), match(0) - { - // Disable some sequences that can lead to security problems + compiled_regex_t(const wchar_t *argv0, const wchar_t *pattern, bool ignore_case, + io_streams_t &streams) + : code(0), match(0) { + // Disable some sequences that can lead to security problems. uint32_t options = PCRE2_NEVER_UTF; #if PCRE2_CODE_UNIT_WIDTH < 32 options |= PCRE2_NEVER_BACKSLASH_C; @@ -421,68 +343,51 @@ struct compiled_regex_t int err_code = 0; PCRE2_SIZE err_offset = 0; - code = pcre2_compile( - PCRE2_SPTR(pattern), - PCRE2_ZERO_TERMINATED, - options | (ignore_case ? PCRE2_CASELESS : 0), - &err_code, - &err_offset, - 0); - if (code == 0) - { - string_error(streams, _(L"%ls: Regular expression compile error: %ls\n"), - argv0, pcre2_strerror(err_code).c_str()); + code = + pcre2_compile(PCRE2_SPTR(pattern), PCRE2_ZERO_TERMINATED, + options | (ignore_case ? PCRE2_CASELESS : 0), &err_code, &err_offset, 0); + if (code == 0) { + string_error(streams, _(L"%ls: Regular expression compile error: %ls\n"), argv0, + pcre2_strerror(err_code).c_str()); string_error(streams, L"%ls: %ls\n", argv0, pattern); string_error(streams, L"%ls: %*ls\n", argv0, err_offset, L"^"); return; } match = pcre2_match_data_create_from_pattern(code, 0); - if (match == 0) - { + if (match == 0) { DIE_MEM(); } } - ~compiled_regex_t() - { - if (match != 0) - { + ~compiled_regex_t() { + if (match != 0) { pcre2_match_data_free(match); } - if (code != 0) - { + if (code != 0) { pcre2_code_free(code); } } }; -class pcre2_matcher_t: public string_matcher_t -{ +class pcre2_matcher_t : public string_matcher_t { const wchar_t *argv0; compiled_regex_t regex; - int report_match(const wchar_t *arg, int pcre2_rc) - { - // Return values: -1 = error, 0 = no match, 1 = match - if (pcre2_rc == PCRE2_ERROR_NOMATCH) - { - if (opts.invert_match && !opts.quiet) - { + int report_match(const wchar_t *arg, int pcre2_rc) { + // Return values: -1 = error, 0 = no match, 1 = match. + if (pcre2_rc == PCRE2_ERROR_NOMATCH) { + if (opts.invert_match && !opts.quiet) { streams.out.append(arg); streams.out.push_back(L'\n'); } return opts.invert_match ? 1 : 0; - } - else if (pcre2_rc < 0) - { - string_error(streams, _(L"%ls: Regular expression match error: %ls\n"), - argv0, pcre2_strerror(pcre2_rc).c_str()); + } else if (pcre2_rc < 0) { + string_error(streams, _(L"%ls: Regular expression match error: %ls\n"), argv0, + pcre2_strerror(pcre2_rc).c_str()); return -1; - } - else if (pcre2_rc == 0) - { + } else if (pcre2_rc == 0) { // The output vector wasn't big enough. Should not happen. string_error(streams, _(L"%ls: Regular expression internal error\n"), argv0); return -1; @@ -493,18 +398,15 @@ class pcre2_matcher_t: public string_matcher_t PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(regex.match); - for (int j = 0; j < pcre2_rc; j++) - { - PCRE2_SIZE begin = ovector[2*j]; - PCRE2_SIZE end = ovector[2*j + 1]; + for (int j = 0; j < pcre2_rc; j++) { + PCRE2_SIZE begin = ovector[2 * j]; + PCRE2_SIZE end = ovector[2 * j + 1]; - if (begin != PCRE2_UNSET && end != PCRE2_UNSET && !opts.quiet) - { - if (opts.index) - { - streams.out.append_format(L"%lu %lu", (unsigned long)(begin + 1), (unsigned long)(end - begin)); - } - else if (end > begin) // may have end < begin if \K is used + if (begin != PCRE2_UNSET && end != PCRE2_UNSET && !opts.quiet) { + if (opts.index) { + streams.out.append_format(L"%lu %lu", (unsigned long)(begin + 1), + (unsigned long)(end - begin)); + } else if (end > begin) // may have end < begin if \K is used { streams.out.append(wcstring(&arg[begin], end - begin)); } @@ -513,77 +415,63 @@ class pcre2_matcher_t: public string_matcher_t } return opts.invert_match ? 0 : 1; - } -public: - pcre2_matcher_t(const wchar_t *argv0_, const wchar_t *pattern, const match_options_t &opts, io_streams_t &streams) + public: + pcre2_matcher_t(const wchar_t *argv0_, const wchar_t *pattern, const match_options_t &opts, + io_streams_t &streams) : string_matcher_t(opts, streams), argv0(argv0_), - regex(argv0_, pattern, opts.ignore_case, streams) - { } + regex(argv0_, pattern, opts.ignore_case, streams) {} - virtual ~pcre2_matcher_t() { } + virtual ~pcre2_matcher_t() {} - bool report_matches(const wchar_t *arg) - { - // A return value of true means all is well (even if no matches were - // found), false indicates an unrecoverable error. - if (regex.code == 0) - { - // pcre2_compile() failed + bool report_matches(const wchar_t *arg) { + // A return value of true means all is well (even if no matches were found), false indicates + // an unrecoverable error. + if (regex.code == 0) { + // pcre2_compile() failed. return false; } int matched = 0; - // See pcre2demo.c for an explanation of this logic + // See pcre2demo.c for an explanation of this logic. PCRE2_SIZE arglen = wcslen(arg); - int rc = report_match(arg, pcre2_match(regex.code, PCRE2_SPTR(arg), arglen, 0, 0, regex.match, 0)); - if (rc < 0) - { - // pcre2 match error + int rc = report_match( + arg, pcre2_match(regex.code, PCRE2_SPTR(arg), arglen, 0, 0, regex.match, 0)); + if (rc < 0) { // pcre2 match error. return false; - } - else if (rc == 0) - { - // no match + } else if (rc == 0) { // no match return true; } matched++; total_matched++; - if (opts.invert_match) - { + if (opts.invert_match) { return true; } - // Report any additional matches + // Report any additional matches. PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(regex.match); - while (opts.all || matched == 0) - { + while (opts.all || matched == 0) { uint32_t options = 0; - PCRE2_SIZE offset = ovector[1]; // Start at end of previous match + PCRE2_SIZE offset = ovector[1]; // start at end of previous match - if (ovector[0] == ovector[1]) - { - if (ovector[0] == arglen) - { + if (ovector[0] == ovector[1]) { + if (ovector[0] == arglen) { break; } options = PCRE2_NOTEMPTY_ATSTART | PCRE2_ANCHORED; } - rc = report_match(arg, pcre2_match(regex.code, PCRE2_SPTR(arg), arglen, offset, options, regex.match, 0)); - if (rc < 0) - { + rc = report_match(arg, pcre2_match(regex.code, PCRE2_SPTR(arg), arglen, offset, options, + regex.match, 0)); + if (rc < 0) { return false; } - if (rc == 0) - { - if (options == 0) - { - // All matches found + if (rc == 0) { + if (options == 0) { // all matches found break; } ovector[1] = offset + 1; @@ -596,96 +484,83 @@ public: } }; -static int string_match(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv) -{ +static int string_match(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv) { const wchar_t *short_options = L"ainvqr"; - const struct woption long_options[] = - { - { L"all", no_argument, 0, 'a'}, - { L"ignore-case", no_argument, 0, 'i'}, - { L"index", no_argument, 0, 'n'}, - { L"invert", no_argument, 0, 'v'}, - { L"quiet", no_argument, 0, 'q'}, - { L"regex", no_argument, 0, 'r'}, - { 0, 0, 0, 0 } - }; + const struct woption long_options[] = {{L"all", no_argument, 0, 'a'}, + {L"ignore-case", no_argument, 0, 'i'}, + {L"index", no_argument, 0, 'n'}, + {L"invert", no_argument, 0, 'v'}, + {L"quiet", no_argument, 0, 'q'}, + {L"regex", no_argument, 0, 'r'}, + {0, 0, 0, 0}}; match_options_t opts; bool regex = false; wgetopter_t w; - for (;;) - { + for (;;) { int c = w.wgetopt_long(argc, argv, short_options, long_options, 0); - if (c == -1) - { + if (c == -1) { break; } - switch (c) - { - case 0: + switch (c) { + case 0: { break; - - case 'a': + } + case 'a': { opts.all = true; break; - - case 'i': + } + case 'i': { opts.ignore_case = true; break; - - case 'n': + } + case 'n': { opts.index = true; break; - - case 'v': + } + case 'v': { opts.invert_match = true; break; - - case 'q': + } + case 'q': { opts.quiet = true; break; - - case 'r': + } + case 'r': { regex = true; break; - - case '?': + } + case '?': { string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]); return BUILTIN_STRING_ERROR; + } } } int i = w.woptind; const wchar_t *pattern; - if ((pattern = string_get_arg_argv(&i, argv)) == 0) - { + if ((pattern = string_get_arg_argv(&i, argv)) == 0) { string_error(streams, STRING_ERR_MISSING, argv[0]); return BUILTIN_STRING_ERROR; } - if (string_args_from_stdin(streams) && argc > i) - { + if (string_args_from_stdin(streams) && argc > i) { string_error(streams, BUILTIN_ERR_TOO_MANY_ARGUMENTS, argv[0]); return BUILTIN_STRING_ERROR; } string_matcher_t *matcher; - if (regex) - { + if (regex) { matcher = new pcre2_matcher_t(argv[0], pattern, opts, streams); - } - else - { + } else { matcher = new wildcard_matcher_t(argv[0], pattern, opts, streams); } const wchar_t *arg; wcstring storage; - while ((arg = string_get_arg(&i, argv, &storage, streams)) != 0) - { - if (!matcher->report_matches(arg)) - { + while ((arg = string_get_arg(&i, argv, &storage, streams)) != 0) { + if (!matcher->report_matches(arg)) { delete matcher; return BUILTIN_STRING_ERROR; } @@ -696,78 +571,67 @@ static int string_match(parser_t &parser, io_streams_t &streams, int argc, wchar return rc; } -struct replace_options_t -{ +struct replace_options_t { bool all; bool ignore_case; bool quiet; - replace_options_t(): all(false), ignore_case(false), quiet(false) { } + replace_options_t() : all(false), ignore_case(false), quiet(false) {} }; -class string_replacer_t -{ -protected: +class string_replacer_t { + protected: const wchar_t *argv0; replace_options_t opts; int total_replaced; io_streams_t &streams; -public: + public: string_replacer_t(const wchar_t *argv0_, const replace_options_t &opts_, io_streams_t &streams_) - : argv0(argv0_), opts(opts_), total_replaced(0), streams(streams_) - { } + : argv0(argv0_), opts(opts_), total_replaced(0), streams(streams_) {} virtual ~string_replacer_t() {} virtual bool replace_matches(const wchar_t *arg) = 0; int replace_count() { return total_replaced; } }; -class literal_replacer_t: public string_replacer_t -{ +class literal_replacer_t : public string_replacer_t { const wchar_t *pattern; const wchar_t *replacement; size_t patlen; -public: + public: literal_replacer_t(const wchar_t *argv0, const wchar_t *pattern_, const wchar_t *replacement_, - const replace_options_t &opts, io_streams_t &streams) + const replace_options_t &opts, io_streams_t &streams) : string_replacer_t(argv0, opts, streams), - pattern(pattern_), replacement(replacement_), patlen(wcslen(pattern)) - { } + pattern(pattern_), + replacement(replacement_), + patlen(wcslen(pattern)) {} - virtual ~literal_replacer_t() { } + virtual ~literal_replacer_t() {} - bool replace_matches(const wchar_t *arg) - { + bool replace_matches(const wchar_t *arg) { wcstring result; - if (patlen == 0) - { + if (patlen == 0) { result = arg; - } - else - { + } else { int replaced = 0; const wchar_t *cur = arg; - while (*cur != L'\0') - { + while (*cur != L'\0') { if ((opts.all || replaced == 0) && - (opts.ignore_case ? wcsncasecmp(cur, pattern, patlen) : wcsncmp(cur, pattern, patlen)) == 0) - { + (opts.ignore_case ? wcsncasecmp(cur, pattern, patlen) + : wcsncmp(cur, pattern, patlen)) == 0) { result += replacement; cur += patlen; replaced++; total_replaced++; - } - else - { + } else { result += *cur; cur++; } } } - if (!opts.quiet) - { + if (!opts.quiet) { streams.out.append(result); streams.out.append(L'\n'); } @@ -775,23 +639,17 @@ public: } }; -class regex_replacer_t: public string_replacer_t -{ +class regex_replacer_t : public string_replacer_t { compiled_regex_t regex; wcstring replacement; - static wcstring interpret_escapes(const wchar_t *orig) - { + static wcstring interpret_escapes(const wchar_t *orig) { wcstring result; - while (*orig != L'\0') - { - if (*orig == L'\\') - { + while (*orig != L'\0') { + if (*orig == L'\\') { orig += read_unquoted_escape(orig, &result, true, false); - } - else - { + } else { result += *orig; orig++; } @@ -800,22 +658,17 @@ class regex_replacer_t: public string_replacer_t return result; } -public: + public: regex_replacer_t(const wchar_t *argv0, const wchar_t *pattern, const wchar_t *replacement_, - const replace_options_t &opts, io_streams_t &streams) + const replace_options_t &opts, io_streams_t &streams) : string_replacer_t(argv0, opts, streams), regex(argv0, pattern, opts.ignore_case, streams), - replacement(interpret_escapes(replacement_)) - { } + replacement(interpret_escapes(replacement_)) {} - virtual ~regex_replacer_t() { } - - bool replace_matches(const wchar_t *arg) - { - // A return value of true means all is well (even if no replacements - // were performed), false indicates an unrecoverable error. - if (regex.code == 0) - { + bool replace_matches(const wchar_t *arg) { + // A return value of true means all is well (even if no replacements were performed), false + // indicates an unrecoverable error. + if (regex.code == 0) { // pcre2_compile() failed return false; } @@ -826,28 +679,19 @@ public: PCRE2_SIZE bufsize = (arglen == 0) ? 16 : 2 * arglen; wchar_t *output = (wchar_t *)malloc(sizeof(wchar_t) * bufsize); int pcre2_rc = 0; - for (;;) - { - if (output == NULL) - { + for (;;) { + if (output == NULL) { DIE_MEM(); } PCRE2_SIZE outlen = bufsize; - pcre2_rc = pcre2_substitute( - regex.code, - PCRE2_SPTR(arg), - arglen, - 0, // start offset - options, - regex.match, - 0, // match context - PCRE2_SPTR(replacement.c_str()), - PCRE2_ZERO_TERMINATED, - (PCRE2_UCHAR *)output, - &outlen); + pcre2_rc = pcre2_substitute(regex.code, PCRE2_SPTR(arg), arglen, + 0, // start offset + options, regex.match, + 0, // match context + PCRE2_SPTR(replacement.c_str()), PCRE2_ZERO_TERMINATED, + (PCRE2_UCHAR *)output, &outlen); - if (pcre2_rc == PCRE2_ERROR_NOMEMORY && bufsize < outlen) - { + if (pcre2_rc == PCRE2_ERROR_NOMEMORY && bufsize < outlen) { bufsize = outlen; // cppcheck-suppress memleakOnRealloc output = (wchar_t *)realloc(output, sizeof(wchar_t) * bufsize); @@ -857,16 +701,12 @@ public: } bool rc = true; - if (pcre2_rc < 0) - { - string_error(streams, _(L"%ls: Regular expression substitute error: %ls\n"), - argv0, pcre2_strerror(pcre2_rc).c_str()); + if (pcre2_rc < 0) { + string_error(streams, _(L"%ls: Regular expression substitute error: %ls\n"), argv0, + pcre2_strerror(pcre2_rc).c_str()); rc = false; - } - else - { - if (!opts.quiet) - { + } else { + if (!opts.quiet) { streams.out.append(output); streams.out.append(L'\n'); } @@ -878,91 +718,77 @@ public: } }; -static int string_replace(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv) -{ +static int string_replace(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv) { const wchar_t *short_options = L"aiqr"; - const struct woption long_options[] = - { - { L"all", no_argument, 0, 'a'}, - { L"ignore-case", no_argument, 0, 'i'}, - { L"quiet", no_argument, 0, 'q'}, - { L"regex", no_argument, 0, 'r'}, - { 0, 0, 0, 0 } - }; + const struct woption long_options[] = {{L"all", no_argument, 0, 'a'}, + {L"ignore-case", no_argument, 0, 'i'}, + {L"quiet", no_argument, 0, 'q'}, + {L"regex", no_argument, 0, 'r'}, + {0, 0, 0, 0}}; replace_options_t opts; bool regex = false; wgetopter_t w; - for (;;) - { + for (;;) { int c = w.wgetopt_long(argc, argv, short_options, long_options, 0); - if (c == -1) - { + if (c == -1) { break; } - switch (c) - { - case 0: + switch (c) { + case 0: { break; - - case 'a': + } + case 'a': { opts.all = true; break; - - case 'i': + } + case 'i': { opts.ignore_case = true; break; - - case 'q': + } + case 'q': { opts.quiet = true; break; - - case 'r': + } + case 'r': { regex = true; break; - - case '?': + } + case '?': { string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]); return BUILTIN_STRING_ERROR; + } } } int i = w.woptind; const wchar_t *pattern, *replacement; - if ((pattern = string_get_arg_argv(&i, argv)) == 0) - { + if ((pattern = string_get_arg_argv(&i, argv)) == 0) { string_error(streams, STRING_ERR_MISSING, argv[0]); return BUILTIN_STRING_ERROR; } - if ((replacement = string_get_arg_argv(&i, argv)) == 0) - { + if ((replacement = string_get_arg_argv(&i, argv)) == 0) { string_error(streams, STRING_ERR_MISSING, argv[0]); return BUILTIN_STRING_ERROR; } - if (string_args_from_stdin(streams) && argc > i) - { + if (string_args_from_stdin(streams) && argc > i) { string_error(streams, BUILTIN_ERR_TOO_MANY_ARGUMENTS, argv[0]); return BUILTIN_STRING_ERROR; } string_replacer_t *replacer; - if (regex) - { + if (regex) { replacer = new regex_replacer_t(argv[0], pattern, replacement, opts, streams); - } - else - { + } else { replacer = new literal_replacer_t(argv[0], pattern, replacement, opts, streams); } const wchar_t *arg; wcstring storage; - while ((arg = string_get_arg(&i, argv, &storage, streams)) != 0) - { - if (!replacer->replace_matches(arg)) - { + while ((arg = string_get_arg(&i, argv, &storage, streams)) != 0) { + if (!replacer->replace_matches(arg)) { delete replacer; return BUILTIN_STRING_ERROR; } @@ -973,115 +799,94 @@ static int string_replace(parser_t &parser, io_streams_t &streams, int argc, wch return rc; } -// Given iterators into a string (forward or reverse), splits the haystack iterators -// about the needle sequence, up to max times. Inserts splits into the output array -// If the iterators are forward, this does the normal thing. -// If the iterators are backward, this returns reversed strings, in reversed order! -// If the needle is empty, split on individual elements (characters) -template -void split_about(ITER haystack_start, ITER haystack_end, - ITER needle_start, ITER needle_end, - wcstring_list_t *output, long max) -{ +/// Given iterators into a string (forward or reverse), splits the haystack iterators +/// about the needle sequence, up to max times. Inserts splits into the output array. +/// If the iterators are forward, this does the normal thing. +/// If the iterators are backward, this returns reversed strings, in reversed order! +/// If the needle is empty, split on individual elements (characters). +template +void split_about(ITER haystack_start, ITER haystack_end, ITER needle_start, ITER needle_end, + wcstring_list_t *output, long max) { long remaining = max; ITER haystack_cursor = haystack_start; - while (remaining > 0 && haystack_cursor != haystack_end) - { + while (remaining > 0 && haystack_cursor != haystack_end) { ITER split_point; - if (needle_start == needle_end) - { - // empty needle, we split on individual elements + if (needle_start == needle_end) { // empty needle, we split on individual elements split_point = haystack_cursor + 1; - } - else - { + } else { split_point = std::search(haystack_cursor, haystack_end, needle_start, needle_end); } - if (split_point == haystack_end) - { - // not found + if (split_point == haystack_end) { // not found break; } output->push_back(wcstring(haystack_cursor, split_point)); remaining--; - // need to skip over the needle for the next search - // note that the needle may be empty + // Need to skip over the needle for the next search note that the needle may be empty. haystack_cursor = split_point + std::distance(needle_start, needle_end); } - // trailing component, possibly empty + // Trailing component, possibly empty. output->push_back(wcstring(haystack_cursor, haystack_end)); } -static int string_split(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv) -{ +static int string_split(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv) { const wchar_t *short_options = L":m:qr"; - const struct woption long_options[] = - { - { L"max", required_argument, 0, 'm'}, - { L"quiet", no_argument, 0, 'q'}, - { L"right", no_argument, 0, 'r'}, - { 0, 0, 0, 0 } - }; + const struct woption long_options[] = {{L"max", required_argument, 0, 'm'}, + {L"quiet", no_argument, 0, 'q'}, + {L"right", no_argument, 0, 'r'}, + {0, 0, 0, 0}}; long max = LONG_MAX; bool quiet = false; bool right = false; wgetopter_t w; - for (;;) - { + for (;;) { int c = w.wgetopt_long(argc, argv, short_options, long_options, 0); - if (c == -1) - { + if (c == -1) { break; } - switch (c) - { - case 0: + switch (c) { + case 0: { break; - - case 'm': - { + } + case 'm': { errno = 0; wchar_t *endptr = 0; max = wcstol(w.woptarg, &endptr, 10); - if (*endptr != L'\0' || errno != 0) - { + if (*endptr != L'\0' || errno != 0) { string_error(streams, BUILTIN_ERR_NOT_NUMBER, argv[0], w.woptarg); return BUILTIN_STRING_ERROR; } break; } - - case 'q': + case 'q': { quiet = true; break; - - case 'r': + } + case 'r': { right = true; break; - - case ':': + } + case ':': { string_error(streams, STRING_ERR_MISSING, argv[0]); return BUILTIN_STRING_ERROR; - - case '?': + } + case '?': { string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]); return BUILTIN_STRING_ERROR; + } } } int i = w.woptind; const wchar_t *sep; - if ((sep = string_get_arg_argv(&i, argv)) == NULL) - { + if ((sep = string_get_arg_argv(&i, argv)) == NULL) { string_error(streams, STRING_ERR_MISSING, argv[0]); return BUILTIN_STRING_ERROR; } const wchar_t *sep_end = sep + wcslen(sep); - if (string_args_from_stdin(streams) && argc > i) - { + if (string_args_from_stdin(streams) && argc > i) { string_error(streams, BUILTIN_ERR_TOO_MANY_ARGUMENTS, argv[0]); return BUILTIN_STRING_ERROR; } @@ -1090,124 +895,104 @@ static int string_split(parser_t &parser, io_streams_t &streams, int argc, wchar size_t arg_count = 0; wcstring storage; const wchar_t *arg; - while ((arg = string_get_arg(&i, argv, &storage, streams)) != 0) - { + while ((arg = string_get_arg(&i, argv, &storage, streams)) != 0) { const wchar_t *arg_end = arg + wcslen(arg); - if (right) - { + if (right) { typedef std::reverse_iterator reverser; - split_about(reverser(arg_end), reverser(arg), - reverser(sep_end), reverser(sep), - &splits, max); - } - else - { - split_about(arg, arg_end, - sep, sep_end, - &splits, max); + split_about(reverser(arg_end), reverser(arg), reverser(sep_end), reverser(sep), &splits, + max); + } else { + split_about(arg, arg_end, sep, sep_end, &splits, max); } arg_count++; } - + // If we are from the right, split_about gave us reversed strings, in reversed order! - if (right) - { - for (size_t j = 0; j < splits.size(); j++) - { + if (right) { + for (size_t j = 0; j < splits.size(); j++) { std::reverse(splits[j].begin(), splits[j].end()); } std::reverse(splits.begin(), splits.end()); } - - if (!quiet) - { - for (wcstring_list_t::const_iterator si = splits.begin(); si != splits.end(); ++si) - { + + if (!quiet) { + for (wcstring_list_t::const_iterator si = splits.begin(); si != splits.end(); ++si) { streams.out.append(*si); streams.out.append(L'\n'); } } - // we split something if we have more split values than args + // We split something if we have more split values than args. return (splits.size() > arg_count) ? BUILTIN_STRING_OK : BUILTIN_STRING_NONE; } -static int string_sub(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv) -{ +static int string_sub(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv) { const wchar_t *short_options = L":l:qs:"; - const struct woption long_options[] = - { - { L"length", required_argument, 0, 'l'}, - { L"quiet", no_argument, 0, 'q'}, - { L"start", required_argument, 0, 's'}, - { 0, 0, 0, 0 } - }; + const struct woption long_options[] = {{L"length", required_argument, 0, 'l'}, + {L"quiet", no_argument, 0, 'q'}, + {L"start", required_argument, 0, 's'}, + {0, 0, 0, 0}}; long start = 0; long length = -1; bool quiet = false; wgetopter_t w; wchar_t *endptr = NULL; - for (;;) - { + for (;;) { int c = w.wgetopt_long(argc, argv, short_options, long_options, 0); - if (c == -1) - { + if (c == -1) { break; } - switch (c) - { - case 0: + switch (c) { + case 0: { break; - - case 'l': + } + case 'l': { errno = 0; length = wcstol(w.woptarg, &endptr, 10); - if (*endptr != L'\0' || (errno != 0 && errno != ERANGE)) - { + if (*endptr != L'\0' || (errno != 0 && errno != ERANGE)) { string_error(streams, BUILTIN_ERR_NOT_NUMBER, argv[0], w.woptarg); return BUILTIN_STRING_ERROR; } - if (length < 0 || errno == ERANGE) - { - string_error(streams, _(L"%ls: Invalid length value '%ls'\n"), argv[0], w.woptarg); + if (length < 0 || errno == ERANGE) { + string_error(streams, _(L"%ls: Invalid length value '%ls'\n"), argv[0], + w.woptarg); return BUILTIN_STRING_ERROR; } break; - - case 'q': + } + case 'q': { quiet = true; break; - - case 's': + } + case 's': { errno = 0; start = wcstol(w.woptarg, &endptr, 10); - if (*endptr != L'\0' || (errno != 0 && errno != ERANGE)) - { + if (*endptr != L'\0' || (errno != 0 && errno != ERANGE)) { string_error(streams, BUILTIN_ERR_NOT_NUMBER, argv[0], w.woptarg); return BUILTIN_STRING_ERROR; } - if (start == 0 || start == LONG_MIN || errno == ERANGE) - { - string_error(streams, _(L"%ls: Invalid start value '%ls'\n"), argv[0], w.woptarg); + if (start == 0 || start == LONG_MIN || errno == ERANGE) { + string_error(streams, _(L"%ls: Invalid start value '%ls'\n"), argv[0], + w.woptarg); return BUILTIN_STRING_ERROR; } break; - - case ':': + } + case ':': { string_error(streams, STRING_ERR_MISSING, argv[0]); return BUILTIN_STRING_ERROR; - - case '?': + } + case '?': { string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]); return BUILTIN_STRING_ERROR; + } } } int i = w.woptind; - if (string_args_from_stdin(streams) && argc > i) - { + if (string_args_from_stdin(streams) && argc > i) { string_error(streams, BUILTIN_ERR_TOO_MANY_ARGUMENTS, argv[0]); return BUILTIN_STRING_ERROR; } @@ -1215,35 +1000,28 @@ static int string_sub(parser_t &parser, io_streams_t &streams, int argc, wchar_t int nsub = 0; const wchar_t *arg; wcstring storage; - while ((arg = string_get_arg(&i, argv, &storage, streams)) != NULL) - { + while ((arg = string_get_arg(&i, argv, &storage, streams)) != NULL) { typedef wcstring::size_type size_type; size_type pos = 0; size_type count = wcstring::npos; wcstring s(arg); - if (start > 0) - { + if (start > 0) { pos = static_cast(start - 1); - } - else if (start < 0) - { - assert(start != LONG_MIN); // checked above + } else if (start < 0) { + assert(start != LONG_MIN); // checked above size_type n = static_cast(-start); pos = n > s.length() ? 0 : s.length() - n; } - if (pos > s.length()) - { + if (pos > s.length()) { pos = s.length(); } - if (length >= 0) - { + if (length >= 0) { count = static_cast(length); } - // note that std::string permits count to extend past end of string - if (!quiet) - { + // Note that std::string permits count to extend past end of string. + if (!quiet) { streams.out.append(s.substr(pos, count)); streams.out.append(L'\n'); } @@ -1253,100 +1031,88 @@ static int string_sub(parser_t &parser, io_streams_t &streams, int argc, wchar_t return (nsub > 0) ? BUILTIN_STRING_OK : BUILTIN_STRING_NONE; } -static int string_trim(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv) -{ +static int string_trim(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv) { const wchar_t *short_options = L":c:lqr"; - const struct woption long_options[] = - { - { L"chars", required_argument, 0, 'c'}, - { L"left", no_argument, 0, 'l'}, - { L"quiet", no_argument, 0, 'q'}, - { L"right", no_argument, 0, 'r'}, - { 0, 0, 0, 0 } - }; + const struct woption long_options[] = {{L"chars", required_argument, 0, 'c'}, + {L"left", no_argument, 0, 'l'}, + {L"quiet", no_argument, 0, 'q'}, + {L"right", no_argument, 0, 'r'}, + {0, 0, 0, 0}}; bool do_left = 0, do_right = 0; bool quiet = false; wcstring chars_to_trim = L" \f\n\r\t"; wgetopter_t w; - for (;;) - { + for (;;) { int c = w.wgetopt_long(argc, argv, short_options, long_options, 0); - if (c == -1) - { + if (c == -1) { break; } - switch (c) - { - case 0: + switch (c) { + case 0: { break; - - case 'c': + } + case 'c': { chars_to_trim = w.woptarg; break; - - case 'l': + } + case 'l': { do_left = true; break; - - case 'q': + } + case 'q': { quiet = true; break; - - case 'r': + } + case 'r': { do_right = true; break; - - case ':': + } + case ':': { string_error(streams, STRING_ERR_MISSING, argv[0]); return BUILTIN_STRING_ERROR; - - case '?': + } + case '?': { string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]); return BUILTIN_STRING_ERROR; + } } } int i = w.woptind; - if (string_args_from_stdin(streams) && argc > i) - { + if (string_args_from_stdin(streams) && argc > i) { string_error(streams, BUILTIN_ERR_TOO_MANY_ARGUMENTS, argv[0]); return BUILTIN_STRING_ERROR; } - /* if neither left or right is specified, we do both */ - if (! do_left && ! do_right) - { + // If neither left or right is specified, we do both. + if (!do_left && !do_right) { do_left = true; do_right = true; } const wchar_t *arg; size_t ntrim = 0; - + wcstring argstr; wcstring storage; - while ((arg = string_get_arg(&i, argv, &storage, streams)) != 0) - { + while ((arg = string_get_arg(&i, argv, &storage, streams)) != 0) { argstr = arg; - // begin and end are respectively the first character to keep on the left, - // and first character to trim on the right. The length is thus end - start. + // Begin and end are respectively the first character to keep on the left, and first + // character to trim on the right. The length is thus end - start. size_t begin = 0, end = argstr.size(); - if (do_right) - { + if (do_right) { size_t last_to_keep = argstr.find_last_not_of(chars_to_trim); end = (last_to_keep == wcstring::npos) ? 0 : last_to_keep + 1; } - if (do_left) - { + if (do_left) { size_t first_to_keep = argstr.find_first_not_of(chars_to_trim); begin = (first_to_keep == wcstring::npos ? end : first_to_keep); } assert(begin <= end && end <= argstr.size()); ntrim += argstr.size() - (end - begin); - if (!quiet) - { + if (!quiet) { streams.out.append(wcstring(argstr, begin, end - begin)); streams.out.append(L'\n'); } @@ -1355,51 +1121,35 @@ static int string_trim(parser_t &parser, io_streams_t &streams, int argc, wchar_ return (ntrim > 0) ? BUILTIN_STRING_OK : BUILTIN_STRING_NONE; } -static const struct string_subcommand -{ +static const struct string_subcommand { const wchar_t *name; int (*handler)(parser_t &, io_streams_t &, int argc, wchar_t **argv); } -string_subcommands[] = -{ - { L"escape", &string_escape }, - { L"join", &string_join }, - { L"length", &string_length }, - { L"match", &string_match }, - { L"replace", &string_replace }, - { L"split", &string_split }, - { L"sub", &string_sub }, - { L"trim", &string_trim }, - { 0, 0 } -}; +string_subcommands[] = { + {L"escape", &string_escape}, {L"join", &string_join}, {L"length", &string_length}, + {L"match", &string_match}, {L"replace", &string_replace}, {L"split", &string_split}, + {L"sub", &string_sub}, {L"trim", &string_trim}, {0, 0}}; -/** - The string builtin, for manipulating strings. -*/ -int builtin_string(parser_t &parser, io_streams_t &streams, wchar_t **argv) -{ +/// The string builtin, for manipulating strings. +int builtin_string(parser_t &parser, io_streams_t &streams, wchar_t **argv) { int argc = builtin_count_args(argv); - if (argc <= 1) - { + if (argc <= 1) { streams.err.append_format(_(L"string: Expected subcommand\n")); builtin_print_help(parser, streams, L"string", streams.err); return BUILTIN_STRING_ERROR; } - if (wcscmp(argv[1], L"-h") == 0 || wcscmp(argv[1], L"--help") == 0) - { + if (wcscmp(argv[1], L"-h") == 0 || wcscmp(argv[1], L"--help") == 0) { builtin_print_help(parser, streams, L"string", streams.err); return BUILTIN_STRING_OK; } const string_subcommand *subcmd = &string_subcommands[0]; - while (subcmd->name != 0 && wcscmp(subcmd->name, argv[1]) != 0) - { + while (subcmd->name != 0 && wcscmp(subcmd->name, argv[1]) != 0) { subcmd++; } - if (subcmd->handler == 0) - { + if (subcmd->handler == 0) { streams.err.append_format(_(L"string: Unknown subcommand '%ls'\n"), argv[1]); builtin_print_help(parser, streams, L"string", streams.err); return BUILTIN_STRING_ERROR; diff --git a/src/builtin_string.h b/src/builtin_string.h new file mode 100644 index 000000000..49247d9c1 --- /dev/null +++ b/src/builtin_string.h @@ -0,0 +1,11 @@ +// Prototypes for functions for executing builtin_string functions. +#ifndef FISH_BUILTIN_STRING_H +#define FISH_BUILTIN_STRING_H + +#include +#include + +class parser_t; + +int builtin_string(parser_t &parser, io_streams_t &streams, wchar_t **argv); +#endif diff --git a/src/builtin_test.cpp b/src/builtin_test.cpp index 9c66238dc..16d0209a1 100644 --- a/src/builtin_test.cpp +++ b/src/builtin_test.cpp @@ -1,70 +1,60 @@ -/** \file builtin_test.cpp Functions defining the test builtin - -Functions used for implementing the test builtin. -Implemented from scratch (yes, really) by way of IEEE 1003.1 as reference. - -*/ -#include -#include -#include -#include +// Functions used for implementing the test builtin. +// +// Implemented from scratch (yes, really) by way of IEEE 1003.1 as reference. #include #include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include -#include "common.h" #include "builtin.h" -#include "wutil.h" // IWYU pragma: keep -#include "proc.h" +#include "common.h" #include "io.h" +#include "proc.h" +#include "wutil.h" // IWYU pragma: keep -enum -{ - BUILTIN_TEST_SUCCESS = STATUS_BUILTIN_OK, - BUILTIN_TEST_FAIL = STATUS_BUILTIN_ERROR -}; - +enum { BUILTIN_TEST_SUCCESS = STATUS_BUILTIN_OK, BUILTIN_TEST_FAIL = STATUS_BUILTIN_ERROR }; int builtin_test(parser_t &parser, io_streams_t &streams, wchar_t **argv); -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 check effective group id - test_filetype_g, // "-g", for set-group-id - test_filetype_h, // "-h", for symbolic links - test_filetype_L, // "-L", same as -h - test_filetype_O, // "-O", for check effective user id - 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 check effective group id + test_filetype_g, // "-g", for set-group-id + test_filetype_h, // "-h", for symbolic links + test_filetype_L, // "-L", same as -h + test_filetype_O, // "-O", for check effective user id + 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 @@ -73,113 +63,95 @@ enum token_t 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 -{ +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_g, L"-g", UNARY_PRIMARY}, - {test_filetype_h, L"-h", UNARY_PRIMARY}, - {test_filetype_L, L"-L", UNARY_PRIMARY}, - {test_filetype_O, L"-O", 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} -}; +} 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_g, L"-g", UNARY_PRIMARY}, + {test_filetype_h, L"-h", UNARY_PRIMARY}, + {test_filetype_L, L"-L", UNARY_PRIMARY}, + {test_filetype_O, L"-O", 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) - { +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 + return &token_infos[0]; // unknown } - -/* Grammar. - - = - - = and/or | - - - = bang | - - - = arg | - arg arg | - '(' ')' - -*/ +// Grammar. +// +// = +// +// = and/or | +// +// +// = bang | +// +// +// = arg | +// arg arg | +// '(' ')' class expression; -class test_parser -{ -private: +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); - } + const wcstring &arg(unsigned int idx) { return strings.at(idx); } -public: - explicit test_parser(const wcstring_list_t &val) : strings(val) - { } + public: + explicit test_parser(const wcstring_list_t &val) : strings(val) {} expression *parse_expression(unsigned int start, unsigned int end); expression *parse_3_arg_expression(unsigned int start, unsigned int end); @@ -196,81 +168,76 @@ public: static expression *parse_args(const wcstring_list_t &args, wcstring &err); }; -struct range_t -{ +struct range_t { unsigned int start; unsigned int end; - range_t(unsigned s, unsigned e) : start(s), end(e) { } + 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) {} -/* Base class for expressions */ -class expression -{ -protected: - expression(token_t what, range_t where) : token(what), range(where) { } - -public: + public: const token_t token; range_t range; - virtual ~expression() { } + virtual ~expression() {} - // evaluate returns true if the expression is true (i.e. BUILTIN_TEST_SUCCESS) + /// 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: +/// 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) { } + 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: +/// 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) - { } + 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: +/// 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) { } + 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: +/// 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 */ + combining_expression(token_t tok, range_t where, const std::vector &exprs, + const std::vector &combs) + : expression(tok, where), subjects(exprs), combiners(combs) { + // We should have one more subject than combiner. assert(subjects.size() == combiners.size() + 1); } - /* We are responsible for destroying our expressions */ - virtual ~combining_expression() - { - for (size_t i=0; i < subjects.size(); i++) - { + // We are responsible for destroying our expressions. + virtual ~combining_expression() { + for (size_t i = 0; i < subjects.size(); i++) { delete subjects[i]; } } @@ -278,18 +245,17 @@ public: bool evaluate(wcstring_list_t &errors); }; -/* Parenthetical expression */ -class parenthetical_expression : public expression -{ -public: +/// 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) { } + 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, ...) -{ +void test_parser::add_error(const wchar_t *fmt, ...) { assert(fmt != NULL); va_list va; va_start(va, fmt); @@ -297,8 +263,7 @@ void test_parser::add_error(const wchar_t *fmt, ...) va_end(va); } -expression *test_parser::error(const wchar_t *fmt, ...) -{ +expression *test_parser::error(const wchar_t *fmt, ...) { assert(fmt != NULL); va_list va; va_start(va, fmt); @@ -307,170 +272,140 @@ expression *test_parser::error(const wchar_t *fmt, ...) return NULL; } -expression *test_parser::parse_unary_expression(unsigned int start, unsigned int end) -{ - if (start >= end) - { +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) - { + if (tok == test_bang) { expr_ref_t subject(parse_unary_expression(start + 1, end)); - if (subject.get()) - { + if (subject.get()) { return new unary_operator(tok, range_t(start, subject->range.end), subject); - } - else - { + } else { return NULL; } - } - else - { + } else { return parse_primary(start, end); } } -/* Parse a combining expression (AND, OR) */ -expression *test_parser::parse_combining_expression(unsigned int start, unsigned int end) -{ - if (start >= end) - return NULL; +/// Parse a combining expression (AND, OR). +expression *test_parser::parse_combining_expression(unsigned int start, unsigned int end) { + if (start >= end) return NULL; std::vector subjects; std::vector combiners; unsigned int idx = start; bool first = true; - while (idx < end) - { - - if (! first) - { - /* This is not the first expression, so we expect a combiner. */ + while (idx < end) { + if (!first) { + // 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) - { + if (combiner != test_combine_and && combiner != test_combine_or) { /* Not a combiner, we're done */ - this->errors.insert(this->errors.begin(), format_string(L"Expected a combining operator like '-a' at index %u", idx)); + this->errors.insert( + this->errors.begin(), + format_string(L"Expected a combining operator like '-a' at index %u", idx)); break; } combiners.push_back(combiner); idx++; } - /* Parse another expression */ + // Parse another expression. expression *expr = parse_unary_expression(idx, end); - if (! expr) - { + if (!expr) { add_error(L"Missing argument at index %u", idx); - if (! first) - { - /* Clean up the dangling combiner, since it never got its right hand expression */ + if (!first) { + // Clean up the dangling combiner, since it never got its right hand expression. combiners.pop_back(); } break; } - /* Go to the end of this expression */ + // Go to the end of this expression. idx = expr->range.end; subjects.push_back(expr); first = false; } - if (! subjects.empty()) - { - /* Our new expression takes ownership of all expressions we created. The token we pass is irrelevant. */ + 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 */ + } else { + // No subjects. return NULL; } } -expression *test_parser::parse_unary_primary(unsigned int start, unsigned int end) -{ - /* We need two arguments */ - if (start >= end) - { +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) - { + 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. */ + // 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; + 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 - */ +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) - { + // We need one argument. + 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) - { + 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 */ + // 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) - { + // 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 */ + // 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. */ + // 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) - { + 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. */ + // 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. */ + } 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) - { + // 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); } @@ -478,199 +413,157 @@ expression *test_parser::parse_unary_primary(unsigned int start, unsigned int en } #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) - { +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. */ + // 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; + 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; +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 */ + // 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; + if (open_paren->tok != test_paren_open) return NULL; - /* Parse a subexpression */ + // Parse a subexpression. expression *subexr_ptr = parse_expression(start + 1, end); - if (! subexr_ptr) - return NULL; + if (!subexr_ptr) return NULL; expr_ref_t subexpr(subexr_ptr); - /* Parse a close paren */ + // Parse a close paren. unsigned close_index = subexpr->range.end; assert(close_index <= end); - if (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) - { + 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); + // 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) - { +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); + 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; } -// See IEEE 1003.1 breakdown of the behavior for different parameter counts -expression *test_parser::parse_3_arg_expression(unsigned int start, unsigned int end) -{ +// See IEEE 1003.1 breakdown of the behavior for different parameter counts. +expression *test_parser::parse_3_arg_expression(unsigned int start, unsigned int end) { assert(end - start == 3); expression *result = NULL; const token_info_t *center_token = token_for_string(arg(start + 1)); - if (center_token->flags & BINARY_PRIMARY) - { + if (center_token->flags & BINARY_PRIMARY) { result = parse_binary_primary(start, end); - } - else if (center_token->tok == test_combine_and || center_token->tok == test_combine_or) - { + } else if (center_token->tok == test_combine_and || center_token->tok == test_combine_or) { expr_ref_t left(parse_unary_expression(start, start + 1)); expr_ref_t right(parse_unary_expression(start + 2, start + 3)); - if (left.get() && right.get()) - { - // Transfer ownership to the vector of subjects + if (left.get() && right.get()) { + // Transfer ownership to the vector of subjects. std::vector combiners(1, center_token->tok); std::vector subjects; subjects.push_back(left.release()); subjects.push_back(right.release()); - result = new combining_expression(center_token->tok, range_t(start, end), subjects, combiners); + result = new combining_expression(center_token->tok, range_t(start, end), subjects, + combiners); } - } - else - { + } else { result = parse_unary_expression(start, end); } return result; } -expression *test_parser::parse_4_arg_expression(unsigned int start, unsigned int end) -{ +expression *test_parser::parse_4_arg_expression(unsigned int start, unsigned int end) { assert(end - start == 4); expression *result = NULL; token_t first_token = token_for_string(arg(start))->tok; - if (first_token == test_bang) - { + if (first_token == test_bang) { expr_ref_t subject(parse_3_arg_expression(start + 1, end)); - if (subject.get()) - { + if (subject.get()) { result = new unary_operator(first_token, range_t(start, subject->range.end), subject); } - } - else if (first_token == test_paren_open) - { + } else if (first_token == test_paren_open) { result = parse_parenthentical(start, end); - } - else - { + } else { result = parse_combining_expression(start, end); } return result; } - -expression *test_parser::parse_expression(unsigned int start, unsigned int end) -{ - if (start >= end) - { +expression *test_parser::parse_expression(unsigned int start, unsigned int end) { + if (start >= end) { return error(L"Missing argument at index %u", start); } unsigned int argc = end - start; - switch (argc) - { - case 0: - assert(0); //should have been caught by the above test + switch (argc) { + case 0: { + assert(0); // should have been caught by the above test return NULL; - - case 1: - { + } + case 1: { return error(L"Missing argument at index %u", start + 1); } - case 2: - { + case 2: { return parse_unary_expression(start, end); } - - case 3: - { + case 3: { return parse_3_arg_expression(start, end); } - - case 4: - { + case 4: { return parse_4_arg_expression(start, end); } - - default: - { - return parse_combining_expression(start, end); - } + default: { 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 */ +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 */ - for (size_t i = 0; i < parser.errors.size(); i++) - { + // Handle errors. + 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'); - // For now we only show the first error + // For now we only show the first error. break; } - if (result) - { - /* It's also an error if there are any unused arguments. This is not detected by parse_expression() */ + if (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()) - { - if (err.empty()) - { - append_format(err, L"test: unexpected argument at index %lu: '%ls'\n", (unsigned long)result->range.end, args.at(result->range.end).c_str()); + if (result->range.end < args.size()) { + if (err.empty()) { + 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; @@ -681,290 +574,262 @@ expression *test_parser::parse_args(const wcstring_list_t &args, wcstring &err) return result; } -bool unary_primary::evaluate(wcstring_list_t &errors) -{ +bool unary_primary::evaluate(wcstring_list_t &errors) { return unary_primary_evaluate(token, arg, errors); } -bool binary_primary::evaluate(wcstring_list_t &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: +bool unary_operator::evaluate(wcstring_list_t &errors) { + switch (token) { + case test_bang: { assert(subject.get()); - return ! subject->evaluate(errors); - default: + 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) - { +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); + 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. */ + // 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()); + assert(!subjects.empty()); size_t idx = 0, max = subjects.size(); bool or_result = false; - while (idx < max) - { - if (or_result) - { - /* Short circuit */ + 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. */ + // 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 */ + 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) - { + // 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 it in. or_result = or_result || and_result; } return or_result; } - - default: + default: { errors.push_back(format_string(L"Unknown token type in %s", __func__)); return BUILTIN_TEST_FAIL; - + } } } -bool parenthetical_expression::evaluate(wcstring_list_t &errors) -{ +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) -{ +// 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) -{ +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: + switch (token) { + case test_string_equal: { return left == right; - - case test_string_not_equal: + } + 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: + } + 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 unary_primary_evaluate(test_expressions::token_t token, const wcstring &arg, wcstring_list_t &errors) -{ +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 + 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 + } + 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 + } + 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 + } + case test_filetype_e: { // "-e", for files that exist return !wstat(arg, &buf); - - case test_filetype_f: // "-f", for for regular files + } + case test_filetype_f: { // "-f", for for regular files return !wstat(arg, &buf) && S_ISREG(buf.st_mode); - - case test_filetype_G: // "-G", for check effective group id + } + case test_filetype_G: { // "-G", for check effective group id return !wstat(arg, &buf) && getegid() == buf.st_gid; - - case test_filetype_g: // "-g", for set-group-id + } + 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 + } + 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_O: // "-O", for check effective user id + } + case test_filetype_O: { // "-O", for check effective user id return !wstat(arg, &buf) && geteuid() == buf.st_uid; - - case test_filetype_p: // "-p", for FIFO + } + case test_filetype_p: { // "-p", for FIFO return !wstat(arg, &buf) && S_ISFIFO(buf.st_mode); - - case test_filetype_S: // "-S", socket + } + case test_filetype_S: { // "-S", socket return !wstat(arg, &buf) && S_ISSOCK(buf.st_mode); - - case test_filesize_s: // "-s", size greater than zero + } + 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 + } + 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 + } + case test_fileperm_r: { // "-r", read permission return !waccess(arg, R_OK); - - case test_fileperm_u: // "-u", whether file is setuid + } + 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 + } + 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 + } + 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 + } + 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: + } + default: { errors.push_back(format_string(L"Unknown token type in %s", __func__)); return false; + } } } - }; -/* - * Evaluate a conditional expression given the arguments. - * If fromtest is set, the caller is the test or [ builtin; - * with the pointer giving the name of the command. - * for POSIX conformance this supports a more limited range - * of functionality. - * - * 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, io_streams_t &streams, wchar_t **argv) -{ +/// Evaluate a conditional expression given the arguments. If fromtest is set, the caller is the +/// test or [ builtin; with the pointer giving the name of the command. for POSIX conformance this +/// supports a more limited range of functionality. +/// +/// 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, io_streams_t &streams, wchar_t **argv) { using namespace test_expressions; - /* The first argument should be the name of the command ('test') */ - if (! argv[0]) - return BUILTIN_TEST_FAIL; + // The first argument should be the name of the command ('test'). + if (!argv[0]) return BUILTIN_TEST_FAIL; - /* Whether we are invoked with bracket '[' or not */ - const bool is_bracket = ! wcscmp(argv[0], L"["); + // Whether we are invoked with bracket '[' or not. + const bool is_bracket = !wcscmp(argv[0], L"["); size_t argc = 0; - while (argv[argc + 1]) - argc++; + while (argv[argc + 1]) argc++; - /* If we're bracket, the last argument ought to be ]; we ignore it. Note that argc is the number of arguments after the command name; thus argv[argc] is the last argument. */ - if (is_bracket) - { - if (! wcscmp(argv[argc], L"]")) - { - /* Ignore the closing bracketp */ + // If we're bracket, the last argument ought to be ]; we ignore it. Note that argc is the number + // of arguments after the command name; thus argv[argc] is the last argument. + if (is_bracket) { + if (!wcscmp(argv[argc], L"]")) { + // Ignore the closing bracketp. argc--; - } - else - { + } else { streams.err.append(L"[: the last argument must be ']'\n"); return BUILTIN_TEST_FAIL; } - } - /* Collect the arguments into a list */ + // Collect the arguments into a list. const wcstring_list_t args(argv + 1, argv + 1 + argc); - switch (argc) - { - case 0: - { - // Per 1003.1, exit false + switch (argc) { + case 0: { + // Per 1003.1, exit false. return BUILTIN_TEST_FAIL; } - case 1: - { - // Per 1003.1, exit true if the arg is non-empty + case 1: { + // Per 1003.1, exit true if the arg is non-empty. return args.at(0).empty() ? BUILTIN_TEST_FAIL : BUILTIN_TEST_SUCCESS; } - default: - { + default: { // 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 streams.err.append(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/src/builtin_test.h b/src/builtin_test.h new file mode 100644 index 000000000..b350cc639 --- /dev/null +++ b/src/builtin_test.h @@ -0,0 +1,9 @@ +// Prototypes for functions for executing builtin_test functions. +#ifndef FISH_BUILTIN_TEST_H +#define FISH_BUILTIN_TEST_H + +class parser_t; +struct io_streams_t; + +int builtin_test(parser_t &parser, io_streams_t &streams, wchar_t **argv); +#endif diff --git a/src/builtin_ulimit.cpp b/src/builtin_ulimit.cpp index 270360233..f873c3854 100644 --- a/src/builtin_ulimit.cpp +++ b/src/builtin_ulimit.cpp @@ -1,241 +1,146 @@ -/** \file builtin_ulimit.c Functions defining the ulimit builtin - -Functions used for implementing the ulimit builtin. - -*/ -#include -#include +// Functions used for implementing the ulimit builtin. #include +#include +#include -#include "fallback.h" // IWYU pragma: keep -#include "util.h" #include "builtin.h" #include "common.h" -#include "wgetopt.h" +#include "fallback.h" // IWYU pragma: keep #include "io.h" +#include "util.h" +#include "wgetopt.h" #include "wutil.h" // IWYU pragma: keep class parser_t; -/** - Struct describing a resource limit -*/ -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; -} -; +/// Struct describing a resource limit. +struct resource_t { + int resource; // resource ID + const wchar_t *desc; // description of resource + wchar_t switch_char; // switch used on commandline to specify resource + int multiplier; // the implicit multiplier used when setting getting values +}; -/** - 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 - } - , +/// 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}, #ifdef RLIMIT_MEMLOCK - { - RLIMIT_MEMLOCK, L"Maximum size that may be locked into memory", L'l', 1024 - } - , + {RLIMIT_MEMLOCK, L"Maximum size that may be locked into memory", L'l', 1024}, #endif #ifdef RLIMIT_RSS - { - RLIMIT_RSS, L"Maximum resident set size", L'm', 1024 - } - , + {RLIMIT_RSS, L"Maximum resident set size", L'm', 1024}, #endif - { - RLIMIT_NOFILE, L"Maximum number of open file descriptors", L'n', 1 - } - , - { - RLIMIT_STACK, L"Maximum stack size", L's', 1024 - } - , - { - RLIMIT_CPU, L"Maximum amount of cpu time in seconds", L't', 1 - } - , + {RLIMIT_NOFILE, L"Maximum number of open file descriptors", L'n', 1}, + {RLIMIT_STACK, L"Maximum stack size", L's', 1024}, + {RLIMIT_CPU, L"Maximum amount of cpu time in seconds", L't', 1}, #ifdef RLIMIT_NPROC - { - RLIMIT_NPROC, L"Maximum number of processes available to a single user", L'u', 1 - } - , + {RLIMIT_NPROC, L"Maximum number of processes available to a single user", L'u', 1}, #endif #ifdef RLIMIT_AS - { - RLIMIT_AS, L"Maximum amount of virtual memory available to the shell", L'v', 1024 - } - , + {RLIMIT_AS, L"Maximum amount of virtual memory available to the shell", L'v', 1024}, #endif - { - 0, 0, 0, 0 - } -} -; + {0, 0, 0, 0}}; -/** - Get the implicit multiplication factor for the specified resource limit -*/ -static int get_multiplier(int what) -{ - int i; - - for (i=0; resource_arr[i].desc; i++) - { - if (resource_arr[i].resource == what) - { +/// Get the implicit multiplication factor for the specified resource limit. +static int get_multiplier(int what) { + for (int i = 0; resource_arr[i].desc; i++) { + if (resource_arr[i].resource == what) { return resource_arr[i].multiplier; } } return -1; } -/** - Return the value for the specified resource limit. This function - does _not_ multiply the limit value by the multiplier constant used - by the commandline ulimit. -*/ -static rlim_t get(int resource, int hard) -{ +/// Return the value for the specified resource limit. This function does _not_ multiply the limit +/// value by the multiplier constant used by the commandline ulimit. +static rlim_t get(int resource, int hard) { struct rlimit 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, io_streams_t &streams) -{ +/// Print the value of the specified resource limit. +static void print(int resource, int hard, io_streams_t &streams) { rlim_t l = get(resource, hard); if (l == RLIM_INFINITY) streams.out.append(L"unlimited\n"); else - streams.out.append_format( L"%d\n", l / get_multiplier(resource)); - + streams.out.append_format(L"%d\n", l / get_multiplier(resource)); } -/** - Print values of all resource limits -*/ -static void print_all(int hard, io_streams_t &streams) -{ +/// Print values of all resource limits. +static void print_all(int hard, io_streams_t &streams) { int i; - int w=0; + int w = 0; - for (i=0; resource_arr[i].desc; i++) - { - w=maxi(w, fish_wcswidth(resource_arr[i].desc)); + for (i = 0; resource_arr[i].desc; i++) { + w = maxi(w, fish_wcswidth(resource_arr[i].desc)); } - for (i=0; resource_arr[i].desc; i++) - { + 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; + 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, ")); + const wchar_t *unit = + ((resource_arr[i].resource == RLIMIT_CPU) + ? L"(seconds, " + : (get_multiplier(resource_arr[i].resource) == 1 ? L"(" : L"(kB, ")); - streams.out.append_format( - L"%-*ls %10ls-%lc) ", - w, - resource_arr[i].desc, - unit, - resource_arr[i].switch_char); + streams.out.append_format(L"%-*ls %10ls-%lc) ", w, resource_arr[i].desc, unit, + resource_arr[i].switch_char); - if (l == RLIM_INFINITY) - { + if (l == RLIM_INFINITY) { streams.out.append(L"unlimited\n"); - } - else - { - streams.out.append_format( L"%d\n", l/get_multiplier(resource_arr[i].resource)); + } else { + streams.out.append_format(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) -{ +/// Returns the description for the specified resource limit. +static const wchar_t *get_desc(int what) { int i; - for (i=0; resource_arr[i].desc; i++) - { - if (resource_arr[i].resource == what) - { + for (i = 0; resource_arr[i].desc; i++) { + if (resource_arr[i].resource == what) { return resource_arr[i].desc; } } return L"Not a resource"; } -/** - Set the new value of the specified resource limit. This function - 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, io_streams_t &streams) -{ +/// Set the new value of the specified resource limit. This function 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, io_streams_t &streams) { struct rlimit ls; getrlimit(resource, &ls); - if (hard) - { + if (hard) { ls.rlim_max = value; } - if (soft) - { + if (soft) { ls.rlim_cur = value; - /* - Do not attempt to set the soft limit higher than the hard limit - */ + // 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)) - { + (value != RLIM_INFINITY && ls.rlim_max != RLIM_INFINITY && value > ls.rlim_max)) { ls.rlim_cur = ls.rlim_max; } } - if (setrlimit(resource, &ls)) - { + if (setrlimit(resource, &ls)) { if (errno == EPERM) - streams.err.append_format(L"ulimit: Permission denied when changing resource of type '%ls'\n", get_desc(resource)); + streams.err.append_format( + L"ulimit: Permission denied when changing resource of type '%ls'\n", + get_desc(resource)); else builtin_wperror(L"ulimit", streams); return 1; @@ -243,190 +148,125 @@ static int set(int resource, int hard, int soft, rlim_t value, io_streams_t &str return 0; } -/** - The ulimit builtin, used for setting resource limits. Defined in - builtin_ulimit.c. -*/ -static int builtin_ulimit(parser_t &parser, io_streams_t &streams, wchar_t **argv) -{ +/// The ulimit builtin, used for setting resource limits. +int builtin_ulimit(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wgetopter_t w; - int hard=0; - int soft=0; + int hard = 0; + int soft = 0; int what = RLIMIT_FSIZE; int report_all = 0; int argc = builtin_count_args(argv); - w.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 - } - } - ; + w.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 = w.wgetopt_long(argc, - argv, - L"aHScdflmnstuvh", - long_options, - &opt_index); - if (opt == -1) - break; + int opt = w.wgetopt_long(argc, argv, L"aHScdflmnstuvh", long_options, &opt_index); + if (opt == -1) break; - switch (opt) - { - case 0: - if (long_options[opt_index].flag != 0) - break; - streams.err.append_format(BUILTIN_ERR_UNKNOWN, - argv[0], - long_options[opt_index].name); + switch (opt) { + case 0: { + if (long_options[opt_index].flag != 0) break; + streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0], + long_options[opt_index].name); builtin_print_help(parser, streams, argv[0], streams.err); - return 1; - - case L'a': - report_all=1; + } + case L'a': { + report_all = 1; break; - - case L'H': - hard=1; + } + case L'H': { + hard = 1; break; - - case L'S': - soft=1; + } + case L'S': { + soft = 1; break; - - case L'c': - what=RLIMIT_CORE; + } + case L'c': { + what = RLIMIT_CORE; break; - - case L'd': - what=RLIMIT_DATA; + } + case L'd': { + what = RLIMIT_DATA; break; - - case L'f': - what=RLIMIT_FSIZE; + } + case L'f': { + what = RLIMIT_FSIZE; break; + } #ifdef RLIMIT_MEMLOCK - case L'l': - what=RLIMIT_MEMLOCK; + case L'l': { + what = RLIMIT_MEMLOCK; break; + } #endif - #ifdef RLIMIT_RSS - case L'm': - what=RLIMIT_RSS; + case L'm': { + what = RLIMIT_RSS; break; + } #endif - - case L'n': - what=RLIMIT_NOFILE; + case L'n': { + what = RLIMIT_NOFILE; break; - - case L's': - what=RLIMIT_STACK; + } + case L's': { + what = RLIMIT_STACK; break; - - case L't': - what=RLIMIT_CPU; + } + case L't': { + what = RLIMIT_CPU; break; - + } #ifdef RLIMIT_NPROC - case L'u': - what=RLIMIT_NPROC; + case L'u': { + what = RLIMIT_NPROC; break; + } #endif - #ifdef RLIMIT_AS - case L'v': - what=RLIMIT_AS; + case L'v': { + what = RLIMIT_AS; break; + } #endif - - case L'h': + case L'h': { builtin_print_help(parser, streams, argv[0], streams.out); return 0; - - case L'?': - builtin_unknown_option(parser, streams, argv[0], argv[w.woptind-1]); + } + case L'?': { + builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]); return 1; + } } } - if (report_all) - { - if (argc - w.woptind == 0) - { + if (report_all) { + if (argc - w.woptind == 0) { print_all(hard, streams); - } - else - { + } else { streams.err.append(argv[0]); streams.err.append(L": Too many arguments\n"); builtin_print_help(parser, streams, argv[0], streams.err); @@ -436,54 +276,32 @@ static int builtin_ulimit(parser_t &parser, io_streams_t &streams, wchar_t **arg return 0; } - switch (argc - w.woptind) - { - case 0: - { - /* - Show current limit value - */ + switch (argc - w.woptind) { + case 0: { // show current limit value print(what, hard, streams); break; } - - case 1: - { - /* - Change current limit value - */ + case 1: { // change current limit value rlim_t new_limit; wchar_t *end; - /* - Set both hard and soft limits if nothing else was specified - */ - if (!(hard+soft)) - { - hard=soft=1; + // Set both hard and soft limits if nothing else was specified. + if (!(hard + soft)) { + hard = soft = 1; } - if (wcscasecmp(argv[w.woptind], L"unlimited")==0) - { + if (wcscasecmp(argv[w.woptind], L"unlimited") == 0) { new_limit = RLIM_INFINITY; - } - else if (wcscasecmp(argv[w.woptind], L"hard")==0) - { + } else if (wcscasecmp(argv[w.woptind], L"hard") == 0) { new_limit = get(what, 1); - } - else if (wcscasecmp(argv[w.woptind], L"soft")==0) - { + } else if (wcscasecmp(argv[w.woptind], L"soft") == 0) { new_limit = get(what, soft); - } - else - { - errno=0; + } else { + errno = 0; new_limit = wcstol(argv[w.woptind], &end, 10); - if (errno || *end) - { - streams.err.append_format(L"%ls: Invalid limit '%ls'\n", - argv[0], - argv[w.woptind]); + if (errno || *end) { + streams.err.append_format(L"%ls: Invalid limit '%ls'\n", argv[0], + argv[w.woptind]); builtin_print_help(parser, streams, argv[0], streams.err); return 1; } @@ -492,15 +310,12 @@ static int builtin_ulimit(parser_t &parser, io_streams_t &streams, wchar_t **arg return set(what, hard, soft, new_limit, streams); } - - default: - { + default: { streams.err.append(argv[0]); streams.err.append(L": Too many arguments\n"); builtin_print_help(parser, streams, argv[0], streams.err); return 1; } - } return 0; } diff --git a/src/builtin_ulimit.h b/src/builtin_ulimit.h new file mode 100644 index 000000000..1e694e07c --- /dev/null +++ b/src/builtin_ulimit.h @@ -0,0 +1,11 @@ +// Prototypes for functions for executing builtin_ulimit functions. +#ifndef FISH_BUILTIN_ULIMIT_H +#define FISH_BUILTIN_ULIMIT_H + +#include +#include + +class parser_t; + +int builtin_ulimit(parser_t &parser, io_streams_t &streams, wchar_t **argv); +#endif From d0fdc807253b59f2b8db0205e7083b26189b353f Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 27 Apr 2016 14:17:29 +0200 Subject: [PATCH 139/363] Make busctl completions useful - More accurate - Fast enough to be usable (previously, this would sometimes take a few seconds) - A bit smaller --- share/completions/busctl.fish | 250 ++++++++++++++-------------------- 1 file changed, 104 insertions(+), 146 deletions(-) diff --git a/share/completions/busctl.fish b/share/completions/busctl.fish index ebbba06fc..ea3ef2765 100644 --- a/share/completions/busctl.fish +++ b/share/completions/busctl.fish @@ -1,140 +1,107 @@ # Fish completions for systemd's "busctl" dbus tool +# TODO: +# One issue is that sometimes these will come to a dead-end e.g. when a particular interface has no properties +# Another is that some busnames aren't accesible by the current user +# but this can't be predicted via the user that owns that name, e.g. `org.freedesktop.login1` +# is usually owned by a root-owned process, yet accessible (at least in part) by normal users -# A ton of functions, all working by the same principle -# __fish_busctl_*: Get the available options for * via busctl "list" or "introspect" -# __fish_busctl_has_*: Return 0 when a * is specified (and echo it), else return 1 -# Unfortunately these become a bit slow as they stack because we don't keep state -# i.e. __fish_busctl_methods needs to know the interface, which needs the object, which needs the busname -# To speed this up, we'd need to either keep state or just assume e.g. the first non-option argument to "call" is a busname - -function __fish_busctl_busnames +# A simple wrapper to call busctl with the correct mode and output +function __fish_busctl + # TODO: If there's a "--address" argument we need to pass that + # We also need to pass the _last_ of these (`busctl --user --system` operates on the system bus) set -l mode if __fish_contains_opt user set mode "--user" else set mode "--system" end - command busctl $mode list --no-legend --no-pager ^/dev/null | while read a b; echo $a; end -end - -function __fish_busctl_has_busname - for busname in (__fish_busctl_busnames) - if contains -- $busname (commandline -opc) - echo $busname - return 0 - end - end - return 1 -end - -function __fish_busctl_has_object - for object in (__fish_busctl_objects) - if contains -- $object (commandline -opc) - echo $object - return 0 - end - end - return 1 -end - -function __fish_busctl_has_interface - for interface in (__fish_busctl_interfaces) - if contains -- $interface (commandline -opc) - echo $interface - return 0 - end - end - return 1 -end - -function __fish_busctl_has_member - for member in (__fish_busctl_members $argv) - if contains -- $member (commandline -opc) - echo $member - return 0 - end - end - return 1 -end - -function __fish_busctl_has_method - __fish_busctl_has_member method -end - -function __fish_busctl_has_property - __fish_busctl_has_member property -end - -function __fish_busctl_has_signature - for signature in (__fish_busctl_signature) - if contains -- $signature (commandline -opc) - echo $signature - return 0 - end - end - return 1 -end - -function __fish_busctl_objects - set -l mode - if __fish_contains_opt user - set mode "--user" - else - set mode "--system" - end - set -l busname (__fish_busctl_has_busname) - command busctl $mode tree --list --no-legend --no-pager $busname ^/dev/null | while read a b; echo $a; end -end - -function __fish_busctl_interfaces - set -l mode - if __fish_contains_opt user - set mode "--user" - else - set mode "--system" - end - set -l busname (__fish_busctl_has_busname) - set -l object (__fish_busctl_has_object) - command busctl $mode introspect --list --no-legend --no-pager $busname $object ^/dev/null | while read a b; echo $a; end + command busctl $mode $argv --no-legend --no-pager ^/dev/null end -function __fish_busctl_members - set -l mode - if __fish_contains_opt user - set mode "--user" - else - set mode "--system" +# Only get the arguments to the actual command, skipping all options and arguments to options +function __fish_busctl_get_command_args + set -l skip + set -l skip_next 0 + set -l cmd (commandline -opc) + for token in $cmd + switch $token + # Options that take arguments - when given without "=", the next token is the arg - skip it + case '--address' '--match' '--expect-reply' '--auto-start' '--allow-interactive-authorization' \ + '--timeout' '--augment-creds' '-H' '--host' '-M' '--machine' + set skip_next 1 + continue + # Skip all options themselves + case '-*' + continue + # Command args only start after a command + case 'status' 'monitor' 'capture' 'tree' 'introspect' 'call' 'get-property' 'set-property' + set -e skip + continue + # These take no arguments, so abort completion + case 'list' 'help' + break + case '*' + if test "$skip_next" -eq 1; or set -q skip + set skip_next 0 + continue + end + echo $token + end end - set -l busname (__fish_busctl_has_busname) - set -l object (__fish_busctl_has_object) - set -l interface (__fish_busctl_has_interface) - command busctl $mode introspect --list --no-legend --no-pager $busname $object $interface ^/dev/null | grep "$argv" | while read a b; echo $a; end | sed -e "s/^\.//" +end + +function __fish_busctl_busnames + __fish_busctl list --acquired | string replace -r '\s+.*$' '' + # Describe unique names (":1.32") with their process (e.g. `:1.32\tsteam`) + __fish_busctl list --unique | string replace -r '\s+\S+\s+(\S+)\s+.*$' '\t$1' end -function __fish_busctl_methods - __fish_busctl_members method +function __fish_busctl_objects -a busname + __fish_busctl tree --list $busname | string replace -r '\s+.*$' '' end -function __fish_busctl_properties - __fish_busctl_members property +function __fish_busctl_interfaces -a busname -a object + __fish_busctl introspect --list $busname $object | string replace -r '\s+.*$' '' +end + +function __fish_busctl_members -a type -a busname -a object -a interface + __fish_busctl introspect --list $busname $object $interface \ + | string match -- "* $type *" | string replace -r '.(\S+) .*' '$1' end -function __fish_busctl_signals - __fish_busctl_members signal +function __fish_busctl_signature -a busname -a object -a interface -a member + __fish_busctl introspect --list $busname $object $interface \ + | string match ".$member *" | while read a b c d; echo $c; end end -function __fish_busctl_signature - set -l mode - if __fish_contains_opt user - set mode "--user" - else - set mode "--system" +# This function completes service/busname, object, interface and then whatever the arguments are +# i.e. if argv[1] is "method", complete methods in the fourth place +function __fish_busctl_soi + set -l args (__fish_busctl_get_command_args) + set -l num (count $args) + switch $num + case 0 # We have nothing, need busname + __fish_busctl_busnames + case 1 # We have busname, need object + __fish_busctl_objects $args + case 2 # We have busname and object, need interface + __fish_busctl_interfaces $args + case '*' # We have busname and object and interface, what we need now depends on the command, so we get it as argument + if test $num -ge 4; and set -q argv[2] # Check >= 4 to repeat the last type for get-property + # Signatures have to be handled specially, because they're dependent on the member (method/property) + # that's at the beginning of the line and prefixed with a "." + # I.e. `busctl introspect` will print ".Capacity", but the argument to give to `get-property` is "Capacity" + if test "$argv[2]" = "signature" + __fish_busctl_signature $args + else + __fish_busctl_members $argv[2] $args + end + else if test $num -ge 3; and set -q argv[1] + __fish_busctl_members $argv[1] $args + else + return 1 + end end - set -l busname (__fish_busctl_has_busname) - set -l object (__fish_busctl_has_object) - set -l interface (__fish_busctl_has_interface) - set -l member (__fish_busctl_has_member) - command busctl $mode introspect --list --no-legend --no-pager $busname $object $interface ^/dev/null | grep "^.$member " | while read a b c d; echo $c; end end ### Commands @@ -145,42 +112,33 @@ complete -A -f -c busctl -n "not __fish_seen_subcommand_from $commands" -a "$com ### Arguments to commands # "status" only takes a single service as argument -complete -f -c busctl -n "__fish_seen_subcommand_from status; and not __fish_busctl_has_busname" -a "(__fish_busctl_busnames)" +complete -f -c busctl -n "__fish_seen_subcommand_from status; and not count (__fish_busctl_get_command_args)" -a "(__fish_busctl_busnames)" # These take multiple services -complete -f -c busctl -n "__fish_seen_subcommand_from monitor capture tree" -a "(__fish_busctl_busnames)" - -# These take "service object interface" (and then maybe something else) -set -l service_object_interface_commands introspect call get-property set-property -complete -f -c busctl -n "__fish_seen_subcommand_from $service_object_interface_commands; and not __fish_busctl_has_busname" -a "(__fish_busctl_busnames)" -complete -f -c busctl -n "__fish_seen_subcommand_from $service_object_interface_commands; and __fish_busctl_has_busname; and not __fish_busctl_has_object" -a "(__fish_busctl_objects)" -# Having an object implies having a busname, so we only need to check has_object; and not has_interface -complete -f -c busctl -n "__fish_seen_subcommand_from $service_object_interface_commands; and __fish_busctl_has_object; and not __fish_busctl_has_interface" -a "(__fish_busctl_interfaces)" - -# Call takes service object interface method signature arguments -# We can't complete the arguments (or we'd need to parse the signature) -complete -f -c busctl -n "__fish_seen_subcommand_from call; and __fish_busctl_has_interface; and not __fish_busctl_has_method" -a "(__fish_busctl_methods)" -complete -f -c busctl -n "__fish_seen_subcommand_from call; and __fish_busctl_has_method; and not __fish_busctl_has_signature" -a "(__fish_busctl_signature)" - -complete -f -c busctl -n "__fish_seen_subcommand_from get-property; and __fish_busctl_has_interface" -a "(__fish_busctl_properties)" -complete -f -c busctl -n "__fish_seen_subcommand_from set-property; and __fish_busctl_has_interface; and not __fish_busctl_has_property" -a "(__fish_busctl_properties)" -complete -f -c busctl -n "__fish_seen_subcommand_from set-property; and __fish_busctl_has_property; and not __fish_busctl_has_signature" -a "(__fish_busctl_signature)" +complete -x -c busctl -n "__fish_seen_subcommand_from monitor capture tree" -a "(__fish_busctl_busnames)" +# Read the busctl_soi calls as "Complete service, then object, then interface and then the arguments +# e.g. `call` takes service object interface method signature arguments +# We can't complete the arguments (without parsing the signature, which can look like "a{sv}" for an array of string-to-variant dictionaries) +complete -x -c busctl -n "__fish_seen_subcommand_from call" -a "(__fish_busctl_soi method signature)" +complete -x -c busctl -n "__fish_seen_subcommand_from get-property" -a "(__fish_busctl_soi property)" +complete -x -c busctl -n "__fish_seen_subcommand_from set-property" -a "(__fish_busctl_soi property signature)" +complete -x -c busctl -n "__fish_seen_subcommand_from introspect" -a "(__fish_busctl_soi)" # Flags # These are incomplete as I have no idea how to complete --address= or --match= -complete -f -c busctl -n "__fish_seen_subcommand_from call get-property" -l quiet +complete -f -c busctl -n "__fish_seen_subcommand_from call" -l quiet -d 'Suppress message payload display' complete -f -c busctl -n "__fish_seen_subcommand_from call get-property" -l verbose -complete -f -c busctl -n "__fish_seen_subcommand_from tree introspect" -l list -complete -f -c busctl -n "__fish_seen_subcommand_from call" -l expect-reply= -a "yes no" -complete -f -c busctl -n "__fish_seen_subcommand_from call" -l auto-start= -a "yes no" -complete -f -c busctl -n "__fish_seen_subcommand_from call" -l allow-interactive-authorization= -a "yes no" -complete -f -c busctl -n "__fish_seen_subcommand_from call" -l timeout= -a "(seq 0 100)" -complete -f -c busctl -n "__fish_seen_subcommand_from list" -l unique -complete -f -c busctl -n "__fish_seen_subcommand_from list" -l acquired -complete -f -c busctl -n "__fish_seen_subcommand_from list" -l activatable -complete -f -c busctl -n "__fish_seen_subcommand_from list" -l show-machine -complete -f -c busctl -n "__fish_seen_subcommand_from list status" -l augment-creds= -a "yes no" +complete -f -c busctl -n "__fish_seen_subcommand_from tree" -l list -d 'Show a flat list instead of a tree' +complete -x -c busctl -n "__fish_seen_subcommand_from call" -l expect-reply -a "yes no" +complete -x -c busctl -n "__fish_seen_subcommand_from call" -l auto-start -a "yes no" -d 'Activate the peer if necessary' +complete -x -c busctl -n "__fish_seen_subcommand_from call" -l allow-interactive-authorization -a "yes no" +complete -x -c busctl -n "__fish_seen_subcommand_from call" -l timeout -a "(seq 0 100)" +complete -f -c busctl -n "__fish_seen_subcommand_from list" -l unique -d 'Only show unique (:X.Y) names' +complete -f -c busctl -n "__fish_seen_subcommand_from list" -l acquired -d 'Only show well-known names' +complete -f -c busctl -n "__fish_seen_subcommand_from list" -l activatable -d 'Only show peers that have not been activated yet but can be' +complete -f -c busctl -n "__fish_seen_subcommand_from list" -l show-machine -d 'Show the machine the peers belong to' +complete -x -c busctl -n "__fish_seen_subcommand_from list status" -l augment-creds -a "yes no" complete -f -c busctl -l user complete -f -c busctl -l system complete -f -c busctl -s H -l host= -a "(__fish_print_hostnames)" From bc35ca6366dde3f1c6974a459e102afa70e4d11f Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 27 Apr 2016 14:17:29 +0200 Subject: [PATCH 140/363] Make busctl completions useful - More accurate - Fast enough to be usable (previously, this would sometimes take a few seconds) - A bit smaller --- share/completions/busctl.fish | 250 ++++++++++++++-------------------- 1 file changed, 104 insertions(+), 146 deletions(-) diff --git a/share/completions/busctl.fish b/share/completions/busctl.fish index ebbba06fc..ea3ef2765 100644 --- a/share/completions/busctl.fish +++ b/share/completions/busctl.fish @@ -1,140 +1,107 @@ # Fish completions for systemd's "busctl" dbus tool +# TODO: +# One issue is that sometimes these will come to a dead-end e.g. when a particular interface has no properties +# Another is that some busnames aren't accesible by the current user +# but this can't be predicted via the user that owns that name, e.g. `org.freedesktop.login1` +# is usually owned by a root-owned process, yet accessible (at least in part) by normal users -# A ton of functions, all working by the same principle -# __fish_busctl_*: Get the available options for * via busctl "list" or "introspect" -# __fish_busctl_has_*: Return 0 when a * is specified (and echo it), else return 1 -# Unfortunately these become a bit slow as they stack because we don't keep state -# i.e. __fish_busctl_methods needs to know the interface, which needs the object, which needs the busname -# To speed this up, we'd need to either keep state or just assume e.g. the first non-option argument to "call" is a busname - -function __fish_busctl_busnames +# A simple wrapper to call busctl with the correct mode and output +function __fish_busctl + # TODO: If there's a "--address" argument we need to pass that + # We also need to pass the _last_ of these (`busctl --user --system` operates on the system bus) set -l mode if __fish_contains_opt user set mode "--user" else set mode "--system" end - command busctl $mode list --no-legend --no-pager ^/dev/null | while read a b; echo $a; end -end - -function __fish_busctl_has_busname - for busname in (__fish_busctl_busnames) - if contains -- $busname (commandline -opc) - echo $busname - return 0 - end - end - return 1 -end - -function __fish_busctl_has_object - for object in (__fish_busctl_objects) - if contains -- $object (commandline -opc) - echo $object - return 0 - end - end - return 1 -end - -function __fish_busctl_has_interface - for interface in (__fish_busctl_interfaces) - if contains -- $interface (commandline -opc) - echo $interface - return 0 - end - end - return 1 -end - -function __fish_busctl_has_member - for member in (__fish_busctl_members $argv) - if contains -- $member (commandline -opc) - echo $member - return 0 - end - end - return 1 -end - -function __fish_busctl_has_method - __fish_busctl_has_member method -end - -function __fish_busctl_has_property - __fish_busctl_has_member property -end - -function __fish_busctl_has_signature - for signature in (__fish_busctl_signature) - if contains -- $signature (commandline -opc) - echo $signature - return 0 - end - end - return 1 -end - -function __fish_busctl_objects - set -l mode - if __fish_contains_opt user - set mode "--user" - else - set mode "--system" - end - set -l busname (__fish_busctl_has_busname) - command busctl $mode tree --list --no-legend --no-pager $busname ^/dev/null | while read a b; echo $a; end -end - -function __fish_busctl_interfaces - set -l mode - if __fish_contains_opt user - set mode "--user" - else - set mode "--system" - end - set -l busname (__fish_busctl_has_busname) - set -l object (__fish_busctl_has_object) - command busctl $mode introspect --list --no-legend --no-pager $busname $object ^/dev/null | while read a b; echo $a; end + command busctl $mode $argv --no-legend --no-pager ^/dev/null end -function __fish_busctl_members - set -l mode - if __fish_contains_opt user - set mode "--user" - else - set mode "--system" +# Only get the arguments to the actual command, skipping all options and arguments to options +function __fish_busctl_get_command_args + set -l skip + set -l skip_next 0 + set -l cmd (commandline -opc) + for token in $cmd + switch $token + # Options that take arguments - when given without "=", the next token is the arg - skip it + case '--address' '--match' '--expect-reply' '--auto-start' '--allow-interactive-authorization' \ + '--timeout' '--augment-creds' '-H' '--host' '-M' '--machine' + set skip_next 1 + continue + # Skip all options themselves + case '-*' + continue + # Command args only start after a command + case 'status' 'monitor' 'capture' 'tree' 'introspect' 'call' 'get-property' 'set-property' + set -e skip + continue + # These take no arguments, so abort completion + case 'list' 'help' + break + case '*' + if test "$skip_next" -eq 1; or set -q skip + set skip_next 0 + continue + end + echo $token + end end - set -l busname (__fish_busctl_has_busname) - set -l object (__fish_busctl_has_object) - set -l interface (__fish_busctl_has_interface) - command busctl $mode introspect --list --no-legend --no-pager $busname $object $interface ^/dev/null | grep "$argv" | while read a b; echo $a; end | sed -e "s/^\.//" +end + +function __fish_busctl_busnames + __fish_busctl list --acquired | string replace -r '\s+.*$' '' + # Describe unique names (":1.32") with their process (e.g. `:1.32\tsteam`) + __fish_busctl list --unique | string replace -r '\s+\S+\s+(\S+)\s+.*$' '\t$1' end -function __fish_busctl_methods - __fish_busctl_members method +function __fish_busctl_objects -a busname + __fish_busctl tree --list $busname | string replace -r '\s+.*$' '' end -function __fish_busctl_properties - __fish_busctl_members property +function __fish_busctl_interfaces -a busname -a object + __fish_busctl introspect --list $busname $object | string replace -r '\s+.*$' '' +end + +function __fish_busctl_members -a type -a busname -a object -a interface + __fish_busctl introspect --list $busname $object $interface \ + | string match -- "* $type *" | string replace -r '.(\S+) .*' '$1' end -function __fish_busctl_signals - __fish_busctl_members signal +function __fish_busctl_signature -a busname -a object -a interface -a member + __fish_busctl introspect --list $busname $object $interface \ + | string match ".$member *" | while read a b c d; echo $c; end end -function __fish_busctl_signature - set -l mode - if __fish_contains_opt user - set mode "--user" - else - set mode "--system" +# This function completes service/busname, object, interface and then whatever the arguments are +# i.e. if argv[1] is "method", complete methods in the fourth place +function __fish_busctl_soi + set -l args (__fish_busctl_get_command_args) + set -l num (count $args) + switch $num + case 0 # We have nothing, need busname + __fish_busctl_busnames + case 1 # We have busname, need object + __fish_busctl_objects $args + case 2 # We have busname and object, need interface + __fish_busctl_interfaces $args + case '*' # We have busname and object and interface, what we need now depends on the command, so we get it as argument + if test $num -ge 4; and set -q argv[2] # Check >= 4 to repeat the last type for get-property + # Signatures have to be handled specially, because they're dependent on the member (method/property) + # that's at the beginning of the line and prefixed with a "." + # I.e. `busctl introspect` will print ".Capacity", but the argument to give to `get-property` is "Capacity" + if test "$argv[2]" = "signature" + __fish_busctl_signature $args + else + __fish_busctl_members $argv[2] $args + end + else if test $num -ge 3; and set -q argv[1] + __fish_busctl_members $argv[1] $args + else + return 1 + end end - set -l busname (__fish_busctl_has_busname) - set -l object (__fish_busctl_has_object) - set -l interface (__fish_busctl_has_interface) - set -l member (__fish_busctl_has_member) - command busctl $mode introspect --list --no-legend --no-pager $busname $object $interface ^/dev/null | grep "^.$member " | while read a b c d; echo $c; end end ### Commands @@ -145,42 +112,33 @@ complete -A -f -c busctl -n "not __fish_seen_subcommand_from $commands" -a "$com ### Arguments to commands # "status" only takes a single service as argument -complete -f -c busctl -n "__fish_seen_subcommand_from status; and not __fish_busctl_has_busname" -a "(__fish_busctl_busnames)" +complete -f -c busctl -n "__fish_seen_subcommand_from status; and not count (__fish_busctl_get_command_args)" -a "(__fish_busctl_busnames)" # These take multiple services -complete -f -c busctl -n "__fish_seen_subcommand_from monitor capture tree" -a "(__fish_busctl_busnames)" - -# These take "service object interface" (and then maybe something else) -set -l service_object_interface_commands introspect call get-property set-property -complete -f -c busctl -n "__fish_seen_subcommand_from $service_object_interface_commands; and not __fish_busctl_has_busname" -a "(__fish_busctl_busnames)" -complete -f -c busctl -n "__fish_seen_subcommand_from $service_object_interface_commands; and __fish_busctl_has_busname; and not __fish_busctl_has_object" -a "(__fish_busctl_objects)" -# Having an object implies having a busname, so we only need to check has_object; and not has_interface -complete -f -c busctl -n "__fish_seen_subcommand_from $service_object_interface_commands; and __fish_busctl_has_object; and not __fish_busctl_has_interface" -a "(__fish_busctl_interfaces)" - -# Call takes service object interface method signature arguments -# We can't complete the arguments (or we'd need to parse the signature) -complete -f -c busctl -n "__fish_seen_subcommand_from call; and __fish_busctl_has_interface; and not __fish_busctl_has_method" -a "(__fish_busctl_methods)" -complete -f -c busctl -n "__fish_seen_subcommand_from call; and __fish_busctl_has_method; and not __fish_busctl_has_signature" -a "(__fish_busctl_signature)" - -complete -f -c busctl -n "__fish_seen_subcommand_from get-property; and __fish_busctl_has_interface" -a "(__fish_busctl_properties)" -complete -f -c busctl -n "__fish_seen_subcommand_from set-property; and __fish_busctl_has_interface; and not __fish_busctl_has_property" -a "(__fish_busctl_properties)" -complete -f -c busctl -n "__fish_seen_subcommand_from set-property; and __fish_busctl_has_property; and not __fish_busctl_has_signature" -a "(__fish_busctl_signature)" +complete -x -c busctl -n "__fish_seen_subcommand_from monitor capture tree" -a "(__fish_busctl_busnames)" +# Read the busctl_soi calls as "Complete service, then object, then interface and then the arguments +# e.g. `call` takes service object interface method signature arguments +# We can't complete the arguments (without parsing the signature, which can look like "a{sv}" for an array of string-to-variant dictionaries) +complete -x -c busctl -n "__fish_seen_subcommand_from call" -a "(__fish_busctl_soi method signature)" +complete -x -c busctl -n "__fish_seen_subcommand_from get-property" -a "(__fish_busctl_soi property)" +complete -x -c busctl -n "__fish_seen_subcommand_from set-property" -a "(__fish_busctl_soi property signature)" +complete -x -c busctl -n "__fish_seen_subcommand_from introspect" -a "(__fish_busctl_soi)" # Flags # These are incomplete as I have no idea how to complete --address= or --match= -complete -f -c busctl -n "__fish_seen_subcommand_from call get-property" -l quiet +complete -f -c busctl -n "__fish_seen_subcommand_from call" -l quiet -d 'Suppress message payload display' complete -f -c busctl -n "__fish_seen_subcommand_from call get-property" -l verbose -complete -f -c busctl -n "__fish_seen_subcommand_from tree introspect" -l list -complete -f -c busctl -n "__fish_seen_subcommand_from call" -l expect-reply= -a "yes no" -complete -f -c busctl -n "__fish_seen_subcommand_from call" -l auto-start= -a "yes no" -complete -f -c busctl -n "__fish_seen_subcommand_from call" -l allow-interactive-authorization= -a "yes no" -complete -f -c busctl -n "__fish_seen_subcommand_from call" -l timeout= -a "(seq 0 100)" -complete -f -c busctl -n "__fish_seen_subcommand_from list" -l unique -complete -f -c busctl -n "__fish_seen_subcommand_from list" -l acquired -complete -f -c busctl -n "__fish_seen_subcommand_from list" -l activatable -complete -f -c busctl -n "__fish_seen_subcommand_from list" -l show-machine -complete -f -c busctl -n "__fish_seen_subcommand_from list status" -l augment-creds= -a "yes no" +complete -f -c busctl -n "__fish_seen_subcommand_from tree" -l list -d 'Show a flat list instead of a tree' +complete -x -c busctl -n "__fish_seen_subcommand_from call" -l expect-reply -a "yes no" +complete -x -c busctl -n "__fish_seen_subcommand_from call" -l auto-start -a "yes no" -d 'Activate the peer if necessary' +complete -x -c busctl -n "__fish_seen_subcommand_from call" -l allow-interactive-authorization -a "yes no" +complete -x -c busctl -n "__fish_seen_subcommand_from call" -l timeout -a "(seq 0 100)" +complete -f -c busctl -n "__fish_seen_subcommand_from list" -l unique -d 'Only show unique (:X.Y) names' +complete -f -c busctl -n "__fish_seen_subcommand_from list" -l acquired -d 'Only show well-known names' +complete -f -c busctl -n "__fish_seen_subcommand_from list" -l activatable -d 'Only show peers that have not been activated yet but can be' +complete -f -c busctl -n "__fish_seen_subcommand_from list" -l show-machine -d 'Show the machine the peers belong to' +complete -x -c busctl -n "__fish_seen_subcommand_from list status" -l augment-creds -a "yes no" complete -f -c busctl -l user complete -f -c busctl -l system complete -f -c busctl -s H -l host= -a "(__fish_print_hostnames)" From d5797c543982a0fbc8201ae21db5009e9447f75d Mon Sep 17 00:00:00 2001 From: David Adam Date: Wed, 27 Apr 2016 20:34:14 +0800 Subject: [PATCH 141/363] env_universal_common.cpp: pass correct flag to fcntl Closes #2955. --- src/env_universal_common.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/env_universal_common.cpp b/src/env_universal_common.cpp index 900a2d75a..6dea9eeb5 100644 --- a/src/env_universal_common.cpp +++ b/src/env_universal_common.cpp @@ -687,7 +687,7 @@ bool env_universal_t::open_temporary_file(const wcstring &directory, wcstring *o result_fd = mkstemp(narrow_str); if (result_fd != -1) { - fcntl(result_fd, F_SETFD, O_CLOEXEC); + fcntl(result_fd, F_SETFD, FD_CLOEXEC); } #endif From ac68c0c878defff6f98766814207e97dc2427981 Mon Sep 17 00:00:00 2001 From: David Adam Date: Wed, 27 Apr 2016 20:34:14 +0800 Subject: [PATCH 142/363] env_universal_common.cpp: pass correct flag to fcntl Closes #2955. (cherry picked from commit d5797c543982a0fbc8201ae21db5009e9447f75d) --- src/env_universal_common.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/env_universal_common.cpp b/src/env_universal_common.cpp index 3964a4d84..8f81aff04 100644 --- a/src/env_universal_common.cpp +++ b/src/env_universal_common.cpp @@ -687,7 +687,7 @@ bool env_universal_t::open_temporary_file(const wcstring &directory, wcstring *o result_fd = mkstemp(narrow_str); if (result_fd != -1) { - fcntl(result_fd, F_SETFD, O_CLOEXEC); + fcntl(result_fd, F_SETFD, FD_CLOEXEC); } #endif From 8a940a2ee77b5e0ce1c98f33467a8bf701e34368 Mon Sep 17 00:00:00 2001 From: Michael Steed Date: Sat, 23 Apr 2016 22:09:17 -0600 Subject: [PATCH 143/363] configure: require at least pcre2-10.21 (cherry picked from commit c4c7983497e7be798c3bd1c97df38e8d1d1a58b8) --- configure.ac | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 537e9dc59..2d60a9a08 100644 --- a/configure.ac +++ b/configure.ac @@ -792,6 +792,7 @@ else AC_MSG_RESULT(no) fi +pcre2_min_version=10.21 EXTRA_PCRE2= AC_ARG_WITH( included-pcre2, @@ -820,8 +821,28 @@ if test "x$included_pcre2" != "xyes"; then # and so AC_CHECK_LIB won't work (can't use a variable as library name) # AC_SEARCH_LIBS will use the existing $LIBS flags with no additional library first AC_SEARCH_LIBS([pcre2_compile_$WCHAR_T_BITS], [], - [ working_pcre2=yes - AC_MSG_NOTICE([using system PCRE2 library]) + [ # pcre2 lib found, check for minimum version + pcre2_version=`$PCRE2_CONFIG --version` + AS_VERSION_COMPARE([$pcre2_version], [$pcre2_min_version], + [ # version < minimum + AC_MSG_NOTICE([system PCRE2 library version $pcre2_version, need $pcre2_min_version or later]) + if test "x$included_pcre2" = "xno"; then + # complain about pcre2 version + AC_MSG_ERROR([system PCRE2 library is too old, but --without-included-pcre2 was given.]) + else + # use the internal version; undo changes to LIBS/CXXFLAGS + included_pcre2=yes + LIBS="$XLIBS" + CXXFLAGS="$XCXXFLAGS" + fi + ], + [ # version == minimum + working_pcre2=yes + ], + [ # version > minimum + working_pcre2=yes + ] + ) ], [ # fail case; undo the changes to LIBS/CXXFLAGS working_pcre2=no @@ -831,7 +852,9 @@ if test "x$included_pcre2" != "xyes"; then ) fi - if test "x$working_pcre2" != "xyes"; then + if test "x$working_pcre2" = "xyes"; then + AC_MSG_NOTICE([using system PCRE2 library]) + else # pcre2 size wrong or pcre2-config not found # is it OK to use the included version? if test "x$included_pcre2" = "xno"; then From 5f4a77a324c7254caf1e486a772c10287f76bd64 Mon Sep 17 00:00:00 2001 From: Michael Steed Date: Sat, 23 Apr 2016 18:12:42 -0600 Subject: [PATCH 144/363] Update usage of pcre2_substitute() for pcre2-10.21 - Set PCRE2_SUBSTITUTE_OVERFLOW_LENGTH to get the required buffer length from pcre2 instead of guessing - Set PCRE2_SUBSTITUTE_EXTENDED to enable extra goodies in the replacement string (cherry picked from commit c2f9d60eb1f04a3aa0b01d785c1e133cac8ecf1d) --- src/builtin_string.cpp | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/src/builtin_string.cpp b/src/builtin_string.cpp index 10508a710..3e03faf2e 100644 --- a/src/builtin_string.cpp +++ b/src/builtin_string.cpp @@ -21,7 +21,6 @@ #include #include -#define MAX_REPLACE_SIZE size_t(1048576) // pcre2_substitute maximum output size in wchar_t #define STRING_ERR_MISSING _(L"%ls: Expected argument\n") /* externs from builtin.cpp */ @@ -801,7 +800,6 @@ public: bool replace_matches(const wchar_t *arg) { - // A return value of true means all is well (even if no replacements // were performed), false indicates an unrecoverable error. if (regex.code == 0) @@ -810,17 +808,18 @@ public: return false; } - uint32_t options = opts.all ? PCRE2_SUBSTITUTE_GLOBAL : 0; + uint32_t options = PCRE2_SUBSTITUTE_OVERFLOW_LENGTH | PCRE2_SUBSTITUTE_EXTENDED | + (opts.all ? PCRE2_SUBSTITUTE_GLOBAL : 0); size_t arglen = wcslen(arg); PCRE2_SIZE bufsize = (arglen == 0) ? 16 : 2 * arglen; wchar_t *output = (wchar_t *)malloc(sizeof(wchar_t) * bufsize); - if (output == NULL) - { - DIE_MEM(); - } int pcre2_rc = 0; for (;;) { + if (output == NULL) + { + DIE_MEM(); + } PCRE2_SIZE outlen = bufsize; pcre2_rc = pcre2_substitute( regex.code, @@ -835,22 +834,12 @@ public: (PCRE2_UCHAR *)output, &outlen); - if (pcre2_rc == PCRE2_ERROR_NOMEMORY) + if (pcre2_rc == PCRE2_ERROR_NOMEMORY && bufsize < outlen) { - if (bufsize < MAX_REPLACE_SIZE) - { - bufsize = std::min(2 * bufsize, MAX_REPLACE_SIZE); - // cppcheck-suppress memleakOnRealloc - output = (wchar_t *)realloc(output, sizeof(wchar_t) * bufsize); - if (output == NULL) - { - DIE_MEM(); - } - continue; - } - string_error(streams, _(L"%ls: Replacement string too large\n"), argv0); - free(output); - return false; + bufsize = outlen; + // cppcheck-suppress memleakOnRealloc + output = (wchar_t *)realloc(output, sizeof(wchar_t) * bufsize); + continue; } break; } From 87d5fa054d06fcf306138a62dc0829720af50243 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Wed, 27 Apr 2016 14:56:46 -0700 Subject: [PATCH 145/363] compile fallback.cpp and util.cpp seperately Don't `#include "*.cpp"` modules in other cpp modules. I already took care of all the builtin_*.cpp modules in my previous change where I restyled the builtin code. This change fixes the two remaining instances of this anti-pattern. --- Makefile.in | 112 ++++++++++++++++++++----------------------------- src/common.cpp | 3 -- 2 files changed, 46 insertions(+), 69 deletions(-) diff --git a/Makefile.in b/Makefile.in index 6af1d21cc..b19fe6bb8 100644 --- a/Makefile.in +++ b/Makefile.in @@ -23,18 +23,21 @@ # applications, install them, and recalculate dependencies. # +# # This is the default value for SHELL but I like to be explicit about such # things. Especially in a project like fish where someone might otherwise # think fish will be used to execute make recipes. +# SHELL = /bin/sh +# # Used by docdir +# PACKAGE_TARNAME = @PACKAGE_TARNAME@ # # Programs # - CXX := @CXX@ INSTALL:=@INSTALL@ SED := @SED@ @@ -43,7 +46,6 @@ SED := @SED@ # # Installation directories # - prefix = @prefix@ exec_prefix = @exec_prefix@ datarootdir = @datarootdir@ @@ -60,7 +62,6 @@ extra_confdir = @extra_confdir@ # # pcre2 # - PCRE2_WIDTH = @WCHAR_T_BITS@ PCRE2_DIR = pcre2-10.21 PCRE2_LIBDIR = $(PCRE2_DIR)/.libs @@ -71,7 +72,6 @@ EXTRA_PCRE2 = @EXTRA_PCRE2@ # # Various flags # - MACROS = -DLOCALEDIR=\"$(localedir)\" -DPREFIX=L\"$(prefix)\" -DDATADIR=L\"$(datadir)\" -DSYSCONFDIR=L\"$(sysconfdir)\" -DBINDIR=L\"$(bindir)\" -DDOCDIR=L\"$(docdir)\" CXXFLAGS = @CXXFLAGS@ -iquote. -iquote./src/ $(MACROS) $(EXTRA_CXXFLAGS) CPPFLAGS = @CPPFLAGS@ @@ -82,29 +82,30 @@ LDFLAGS_FISH = ${LDFLAGS} @LDFLAGS_FISH@ # # Set to 1 if we have gettext # - HAVE_GETTEXT=@HAVE_GETTEXT@ # # Set to 1 if we have doxygen # - HAVE_DOXYGEN=@HAVE_DOXYGEN@ # # All objects that the system needs to build fish, except fish.o # - -FISH_OBJS := obj/function.o obj/builtin.o obj/builtin_commandline.o obj/builtin_complete.o obj/builtin_jobs.o obj/builtin_printf.o obj/builtin_set.o obj/builtin_set_color.o obj/builtin_string.o obj/builtin_test.o obj/builtin_ulimit.o obj/complete.o obj/env.o obj/exec.o \ - obj/expand.o obj/highlight.o obj/history.o obj/kill.o obj/parser.o \ - obj/proc.o obj/reader.o obj/sanity.o obj/tokenizer.o obj/wildcard.o \ - obj/wgetopt.o obj/wutil.o obj/input.o obj/output.o obj/intern.o \ - obj/env_universal_common.o obj/input_common.o obj/event.o obj/signal.o \ - obj/io.o obj/parse_util.o obj/common.o obj/screen.o obj/path.o \ - obj/autoload.o obj/parser_keywords.o obj/iothread.o obj/color.o \ - obj/postfork.o obj/parse_tree.o \ - obj/parse_productions.o obj/parse_execution.o obj/pager.o obj/utf8.o \ - obj/fish_version.o obj/wcstringutil.o +FISH_OBJS := obj/autoload.o obj/builtin.o obj/builtin_commandline.o \ + obj/builtin_complete.o obj/builtin_jobs.o obj/builtin_printf.o \ + obj/builtin_set.o obj/builtin_set_color.o obj/builtin_string.o \ + obj/builtin_test.o obj/builtin_ulimit.o obj/color.o obj/common.o \ + obj/complete.o obj/env.o obj/env_universal_common.o obj/event.o \ + obj/exec.o obj/expand.o obj/fallback.o obj/fish_version.o \ + obj/function.o obj/highlight.o obj/history.o obj/input.o \ + obj/input_common.o obj/intern.o obj/io.o obj/iothread.o obj/kill.o \ + obj/output.o obj/pager.o obj/parse_execution.o \ + obj/parse_productions.o obj/parse_tree.o obj/parse_util.o \ + obj/parser.o obj/parser_keywords.o obj/path.o obj/postfork.o \ + obj/proc.o obj/reader.o obj/sanity.o obj/screen.o obj/signal.o \ + obj/tokenizer.o obj/utf8.o obj/util.o obj/wcstringutil.o \ + obj/wgetopt.o obj/wildcard.o obj/wutil.o FISH_INDENT_OBJS := obj/fish_indent.o obj/print_help.o $(FISH_OBJS) @@ -113,7 +114,6 @@ FISH_INDENT_OBJS := obj/fish_indent.o obj/print_help.o $(FISH_OBJS) # FISH_TESTS_OBJS := $(FISH_OBJS) obj/fish_tests.o - # # All of the sources that produce object files # (that is, are not themselves #included in other source files) @@ -121,7 +121,6 @@ FISH_TESTS_OBJS := $(FISH_OBJS) obj/fish_tests.o FISH_ALL_OBJS := $(sort $(FISH_OBJS) $(FISH_INDENT_OBJS) $(FISH_TESTS_OBJS) \ obj/fish.o obj/key_reader.o) - # # Files containing user documentation # @@ -130,70 +129,58 @@ FISH_ALL_OBJS := $(sort $(FISH_OBJS) $(FISH_INDENT_OBJS) $(FISH_TESTS_OBJS) \ # These files are the source files, they contain a few @FOO@-style substitutions # Note that this order defines the order that they appear in the documentation # - -HDR_FILES_SRC := doc_src/index.hdr.in doc_src/tutorial.hdr doc_src/design.hdr doc_src/license.hdr doc_src/commands.hdr.in doc_src/faq.hdr +HDR_FILES_SRC := doc_src/index.hdr.in doc_src/tutorial.hdr doc_src/design.hdr \ + doc_src/license.hdr doc_src/commands.hdr.in doc_src/faq.hdr # # These are the generated result files # - HDR_FILES := $(HDR_FILES_SRC:.hdr.in=.hdr) # Use a pattern rule so that Make knows to only issue one invocation # per http://www.gnu.org/software/make/manual/make.html#Pattern-Intro - # # Files containing documentation for external commands. # - HELP_SRC := $(wildcard doc_src/*.txt) # # HTML includes needed for HTML help # - -HTML_SRC := doc_src/user_doc.header.html doc_src/user_doc.footer.html doc_src/user_doc.css +HTML_SRC := doc_src/user_doc.header.html doc_src/user_doc.footer.html \ + doc_src/user_doc.css # # Files in the test directory # - TEST_IN := $(wildcard tests/test*.in) # # Files in ./share/completions/ # - COMPLETIONS_DIR_FILES := $(wildcard share/completions/*.fish) share/completions/..fish - # # Files in ./share/functions/ # - FUNCTIONS_DIR_FILES := $(wildcard share/functions/*.fish) - # # Programs to install # - PROGRAMS := fish fish_indent # # Manual pages to install # - MANUALS := $(addsuffix .1, $(addprefix share/man/man1/, \ $(PROGRAMS))) - # # All translation message catalogs # - TRANSLATIONS_SRC := $(wildcard po/*.po) ifdef HAVE_GETTEXT TRANSLATIONS := $(TRANSLATIONS_SRC:.po=.gmo) @@ -204,7 +191,6 @@ endif # # If Doxygen is not available, don't attempt to build the documentation # - ifeq ($(HAVE_DOXYGEN), 1) user_doc=doc share_man=share/man @@ -216,7 +202,6 @@ endif # # Make everything needed for installing fish # - all: $(PROGRAMS) $(user_doc) $(share_man) $(TRANSLATIONS) fish.pc share/__fish_build_paths.fish @echo fish has now been built. @echo Use \'$(MAKE) install\' to install fish. @@ -225,7 +210,6 @@ all: $(PROGRAMS) $(user_doc) $(share_man) $(TRANSLATIONS) fish.pc share/__fish_b # # Pull version information # - FISH-BUILD-VERSION-FILE: FORCE @./build_tools/git_version_gen.sh -include FISH-BUILD-VERSION-FILE @@ -233,24 +217,20 @@ CXXFLAGS += -DFISH_BUILD_VERSION=\"$(FISH_BUILD_VERSION)\" .PHONY: FORCE obj/fish_version.o: FISH-BUILD-VERSION-FILE - # # These dependencies make sure that autoconf and configure are run # when the source code for the build configuration has changed. # - configure: configure.ac ./config.status --recheck Makefile: Makefile.in configure ./config.status - # # Build fish with some debug flags specified. This is GCC specific, # and should only be used when debuging fish. # - prof: EXTRA_CXXFLAGS += -pg prof: LDFLAGS += -pg prof: all @@ -282,10 +262,12 @@ doc/refman.pdf: doc mv refman.pdf ..; rm -r doc/latex; +# # Prep the environment for running the unit tests. When specifying DESTDIR on # the command line (e.g., `make DESTDIR=/usr/local/`) you must have previously # installed fish using the same prefix; e.g., `./configure --prefix=/usr/local` # followed by `make install`. +# test-prep: rm -rf test mkdir test test/data test/home test/temp @@ -296,10 +278,12 @@ else endif .PHONY: test-prep +# # The test target runs both the low level code tests and the high level script # tests. # # Note that doing `make DESTDIR=/some/path/ test` overrides this assignment. +# test: DESTDIR = $(PWD)/test/root/ test: prefix = . test: test-prep install-force test_low_level test_high_level @@ -307,13 +291,16 @@ test: test-prep install-force test_low_level test_high_level @rm -rf /tmp/is_potential_path_test .PHONY: test +# # We want the various tests to run serially so their output doesn't mix # We can do that by adding ordering dependencies based on what goals are being used. - +# test_goals := test_low_level test_fishscript test_interactive +# # The following variables define targets that depend on the tests. If any more targets # are added that depend, directly or indirectly, on tests, they need to be recorded here. +# test_test_deps = test_low_level $(test_high_level_test_deps) test_high_level_test_deps = test_fishscript test_interactive @@ -339,7 +326,6 @@ test_interactive: $(call filter_up_to,test_interactive,$(active_test_goals)) # commands.hdr collects documentation on all commands, functions and # builtins # - doc_src/commands.hdr:$(HELP_SRC) doc_src/commands.hdr.in -rm command_list.tmp command_list_toc.tmp $@ for i in `printf "%s\n" $(HELP_SRC)|sort`; do \ @@ -355,7 +341,6 @@ doc_src/commands.hdr:$(HELP_SRC) doc_src/commands.hdr.in mv command_list_toc.tmp command_list_toc.txt cat $@.in | awk '{if ($$0 ~ /@command_list_toc@/) { system("cat command_list_toc.txt"); } else if ($$0 ~ /@command_list@/){ system("cat command_list.txt");} else{ print $$0;}}' >$@ - toc.txt: $(HDR_FILES:index.hdr=index.hdr.in) -rm toc.tmp $@ # Ugly hack to set the toc initial title for the main page @@ -384,7 +369,6 @@ doc_src/index.hdr: toc.txt doc_src/index.hdr.in # colour defaults from __fish_config_interactive to set the docs colours when # used in a 'cli' style context. # - lexicon.txt: doc_src/commands.hdr $(FUNCTIONS_DIR_FILES) $(COMPLETIONS_DIR_FILES) share/functions/__fish_config_interactive.fish -rm lexicon.tmp lexicon_catalog.tmp lexicon_catalog.txt $@ # Scan sources for commands/functions/binaries/colours. If GNU sed was portable, this could be much smarter. @@ -415,7 +399,6 @@ lexicon.txt: doc_src/commands.hdr $(FUNCTIONS_DIR_FILES) $(COMPLETIONS_DIR_FILES # HTML, a style context can be applied through the /fish{style} block and # providing suitable CSS in user_doc.css.in # - lexicon_filter: lexicon.txt lexicon_filter.in -rm $@.tmp $@ # Set the shebang as sed can reside in multiple places. @@ -437,7 +420,6 @@ lexicon_filter: lexicon.txt lexicon_filter.in # file that can be parsed by Doxygen to generate the user # documentation. # - doc.h: $(HDR_FILES) cat $(HDR_FILES) >$@ @@ -447,14 +429,15 @@ doc.h: $(HDR_FILES) # internal help functions, that can be parsed to Doxygen to generate # the internal help function text. # - %.doxygen:%.txt echo "/** \page " `basename $*` >$@; cat $*.txt >>$@; echo "*/" >>$@ +# # Depend on Makefile because I don't see a better way of rebuilding # if any of the paths change. +# %: %.in Makefile FISH-BUILD-VERSION-FILE $(SED) <$< >$@ \ -e "s,@sysconfdir\@,$(sysconfdir),g" \ @@ -471,14 +454,12 @@ doc.h: $(HDR_FILES) # # Compile translation files to binary format # - %.gmo: msgfmt -o $@ $*.po # # Update existing po file or copy messages.pot # - %.po:messages.pot if test -f $*.po; then \ msgmerge -U --backup=existing $*.po messages.pot;\ @@ -489,7 +470,6 @@ doc.h: $(HDR_FILES) # # Create a template translation object # - messages.pot: src/*.cpp src/*.h share/completions/*.fish share/functions/*.fish xgettext -k_ -kN_ src/*.cpp src/*.h -o messages.pot xgettext -j -k_ -kN_ -k--description -LShell --from-code=UTF-8 share/completions/*.fish share/functions/*.fish -o messages.pot @@ -524,7 +504,6 @@ endif # # There ought to be something simpler. # - share/man: $(HELP_SRC) lexicon_filter -mkdir share/man touch share/man @@ -540,7 +519,6 @@ share/man: $(HELP_SRC) lexicon_filter # Check for an incompatible installed fish version, and fail with an # error if found # - check-uninstall: if test -f $(DESTDIR)$(sysconfdir)/fish.d/fish_function.fish -o -f $(DESTDIR)$(sysconfdir)/fish.d/fish_complete.fish; then \ echo;\ @@ -596,7 +574,6 @@ check-legacy-binaries: # darcs repo doesn't preserve the executable bit, so this needs to be # run after checkout. # - install-sh: if test -x $@; then true; else chmod 755 $@; fi .PHONY: install-sh @@ -604,7 +581,6 @@ install-sh: # # Try to install after checking for incompatible installed versions. # - install: all install-sh check-uninstall install-force check-legacy-binaries @echo fish is now installed on your system. @echo To run fish, type \'fish\' in your terminal. @@ -716,11 +692,9 @@ install-force: all install-translations done; .PHONY: install-force - # # Uninstall this fish version # - uninstall: uninstall-translations -for i in $(PROGRAMS); do \ rm -f $(DESTDIR)$(bindir)/$$i; \ @@ -747,7 +721,6 @@ uninstall: uninstall-translations # the sysadmin. But if 'make install' detects a file confligt, it # suggests using this target. # - uninstall-legacy: uninstall -rm -f $(DESTDIR)$(sysconfdir)/fish.d/fish_interactive.fish -rm -f $(DESTDIR)$(sysconfdir)/fish.d/fish_complete.fish @@ -781,7 +754,6 @@ uninstall-translations: rm -f $(DESTDIR)$(localedir)/*/LC_MESSAGES/fish.mo .PHONY: uninstall-translations - # # The build rules for all the commands # @@ -801,8 +773,7 @@ obj: # # Build the fish program. # - -fish: $(FISH_OBJS) obj/fish.o $(EXTRA_PCRE2) +fish: obj/fish.o $(FISH_OBJS) $(EXTRA_PCRE2) $(CXX) $(CXXFLAGS) $(LDFLAGS_FISH) $(FISH_OBJS) obj/fish.o $(LIBS) -o $@ $(PCRE2_LIB): $(PCRE2_H) @@ -829,7 +800,6 @@ fish_indent: $(FISH_INDENT_OBJS) $(EXTRA_PCRE2) key_reader: $(FISH_OBJS) $(EXTRA_PCRE2) obj/key_reader.o $(CXX) $(CXXFLAGS) $(LDFLAGS_FISH) $^ $(LIBS) -o $@ - # # Update dependencies # Unfortunately makedepend cannot handle source files in one directory @@ -852,15 +822,19 @@ depend: ./config.status .PHONY: depend +# # Lint the code. This only deals with C++ files. +# lint: build_tools/lint.fish $(CXX) $(CXXFLAGS) lint-all: build_tools/lint.fish $(CXX) --all $(CXXFLAGS) .PHONY: lint lint-all +# # Run the code through the style refomatter. This handles both C++ files and # fish scripts (*.fish). +# style: build_tools/style.fish style-all: @@ -871,19 +845,22 @@ style-all: # Cleanup targets # +# # Restore the source tree to the state right after extracting a tarball. +# distclean: clean $(MAKE) -C $(PCRE2_DIR) distclean || true rm -f config.status config.log config.h Makefile osx/config.h .PHONY: distclean - +# # Remove everything built by the Makefile, but not things that are created by # the configure script. # # Don't delete the docs unless we have Doxygen installed We provide pre-built # docs in the tarball, and if they get deleted we won't be able to regenerate # them. +# clean: $(MAKE) -C $(PCRE2_DIR) clean || true rm -f obj/*.o *.o doc.h doc.tmp @@ -958,8 +935,8 @@ obj/builtin_ulimit.o: src/signal.h src/io.h src/util.h src/wgetopt.h obj/builtin_ulimit.o: src/wutil.h obj/color.o: src/color.h src/common.h config.h src/fallback.h src/signal.h obj/common.o: config.h src/signal.h src/fallback.h src/wutil.h src/common.h -obj/common.o: src/expand.h src/parse_constants.h src/wildcard.h -obj/common.o: src/complete.h src/util.cpp src/util.h src/fallback.cpp +obj/common.o: src/expand.h src/parse_constants.h src/util.h src/wildcard.h +obj/common.o: src/complete.h obj/complete.o: src/fallback.h config.h src/signal.h src/util.h obj/complete.o: src/wildcard.h src/common.h src/expand.h obj/complete.o: src/parse_constants.h src/complete.h src/proc.h src/io.h @@ -990,6 +967,7 @@ obj/expand.o: src/wutil.h src/env.h src/proc.h src/io.h src/parse_tree.h obj/expand.o: src/tokenizer.h src/parse_constants.h src/path.h src/expand.h obj/expand.o: src/wildcard.h src/complete.h src/exec.h src/iothread.h obj/expand.o: src/parse_util.h +obj/fallback.o: config.h src/signal.h src/fallback.h src/util.h obj/fish.o: config.h src/fallback.h src/signal.h src/common.h src/reader.h obj/fish.o: src/complete.h src/highlight.h src/env.h src/color.h obj/fish.o: src/parse_constants.h src/builtin.h src/function.h src/event.h @@ -1112,6 +1090,8 @@ obj/signal.o: src/parse_tree.h src/tokenizer.h obj/tokenizer.o: src/fallback.h config.h src/signal.h src/common.h obj/tokenizer.o: src/wutil.h src/tokenizer.h obj/utf8.o: src/utf8.h +obj/util.o: src/fallback.h config.h src/signal.h src/util.h src/common.h +obj/util.o: src/wutil.h obj/wcstringutil.o: src/common.h config.h src/fallback.h src/signal.h obj/wcstringutil.o: src/wcstringutil.h obj/wgetopt.o: config.h src/common.h src/fallback.h src/signal.h diff --git a/src/common.cpp b/src/common.cpp index 3f738ec05..75f3c945a 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -42,9 +42,6 @@ parts of fish. #include "expand.h" #include "wildcard.h" -#include "util.cpp" -#include "fallback.cpp" - #define NOT_A_WCHAR (static_cast(WEOF)) struct termios shell_modes; From 108d66211f721549a2e149a1942782ab83175759 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Wed, 27 Apr 2016 15:28:34 -0700 Subject: [PATCH 146/363] restyle color module to match project style Reduces lint errors from 16 to 14 (-13%). Line count from 558 to 463 (-17%). Another step in resolving issue #2902. --- src/color.cpp | 339 +++++++++++++++++++++----------------------------- src/color.h | 176 ++++++++++---------------- 2 files changed, 210 insertions(+), 305 deletions(-) diff --git a/src/color.cpp b/src/color.cpp index 6ff52e352..006518052 100644 --- a/src/color.cpp +++ b/src/color.cpp @@ -2,160 +2,154 @@ #include #include #include +#include // IWYU pragma: keep #include "color.h" -#include "fallback.h" // IWYU pragma: keep #include "common.h" +#include "fallback.h" // IWYU pragma: keep -bool rgb_color_t::try_parse_special(const wcstring &special) -{ +bool rgb_color_t::try_parse_special(const wcstring &special) { memset(&data, 0, 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 - { + } else { this->type = type_none; } return this->type != type_none; } -static int parse_hex_digit(wchar_t x) -{ - switch (x) - { - case L'0': +static int parse_hex_digit(wchar_t x) { + switch (x) { + case L'0': { return 0x0; - case L'1': + } + case L'1': { return 0x1; - case L'2': + } + case L'2': { return 0x2; - case L'3': + } + case L'3': { return 0x3; - case L'4': + } + case L'4': { return 0x4; - case L'5': + } + case L'5': { return 0x5; - case L'6': + } + case L'6': { return 0x6; - case L'7': + } + case L'7': { return 0x7; - case L'8': + } + case L'8': { return 0x8; - case L'9': + } + case L'9': { return 0x9; + } case L'a': - case L'A': + case L'A': { return 0xA; + } case L'b': - case L'B': + case L'B': { return 0xB; + } case L'c': - case L'C': + case L'C': { return 0xC; + } case L'd': - case L'D': + case L'D': { return 0xD; + } case L'e': - case L'E': + case L'E': { return 0xE; + } case L'f': - case L'F': + case L'F': { return 0xF; - default: - return -1; + } + default: { return -1; } } } -static unsigned long squared_difference(long p1, long p2) -{ +static unsigned long squared_difference(long p1, long p2) { unsigned long diff = (unsigned long)labs(p1 - 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) - { + 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) { best_index = idx; best_distance = distance; } } return best_index; - } -bool rgb_color_t::try_parse_rgb(const wcstring &name) -{ +bool rgb_color_t::try_parse_rgb(const wcstring &name) { memset(&data, 0, sizeof data); - /* We support the following style of rgb formats (case insensitive): - #FA3 - #F3A035 - FA3 - F3A035 - */ - + // We support the following style of rgb formats (case insensitive): + // #FA3 + // #F3A035 + // FA3 + // F3A035 size_t digit_idx = 0, len = name.size(); - /* Skip any leading # */ - if (len > 0 && name.at(0) == L'#') - digit_idx++; + // Skip any leading #. + if (len > 0 && name.at(0) == L'#') digit_idx++; bool success = false; size_t i; - if (len - digit_idx == 3) - { - // type FA3 - for (i=0; i < 3; i++) - { + if (len - digit_idx == 3) { + // Format: FA3 + for (i = 0; i < 3; i++) { int val = parse_hex_digit(name.at(digit_idx++)); if (val < 0) break; - data.color.rgb[i] = val*16+val; + data.color.rgb[i] = val * 16 + val; } success = (i == 3); - } - else if (len - digit_idx == 6) - { - // type F3A035 - for (i=0; i < 3; i++) - { + } else if (len - digit_idx == 6) { + // Format: F3A035 + 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; - data.color.rgb[i] = hi*16+lo; + data.color.rgb[i] = hi * 16 + lo; } success = (i == 3); } - if (success) - { + if (success) { this->type = type_rgb; } return success; } -struct named_color_t -{ - const wchar_t * name; +struct named_color_t { + const wchar_t *name; unsigned char idx; unsigned char rgb[3]; }; -static const named_color_t named_colors[] = -{ +static const named_color_t named_colors[] = { {L"black", 0, {0, 0, 0}}, {L"red", 1, {0xFF, 0, 0}}, {L"green", 2, {0, 0xFF, 0}}, @@ -178,13 +172,11 @@ static const named_color_t named_colors[] = {L"white", 15, {0xFF, 0xFF, 0xFF}}, }; -wcstring_list_t rgb_color_t::named_color_names(void) -{ +wcstring_list_t rgb_color_t::named_color_names(void) { size_t count = sizeof named_colors / sizeof *named_colors; wcstring_list_t result; result.reserve(1 + count); - for (size_t i=0; i < count; i++) - { + for (size_t i = 0; i < count; i++) { result.push_back(named_colors[i].name); } // "normal" isn't really a color and does not have a color palette index or @@ -196,14 +188,11 @@ wcstring_list_t rgb_color_t::named_color_names(void) return result; } -bool rgb_color_t::try_parse_named(const wcstring &str) -{ +bool rgb_color_t::try_parse_named(const wcstring &str) { memset(&data, 0, 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; @@ -212,166 +201,124 @@ 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::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::reset() { return rgb_color_t(type_reset); } -rgb_color_t rgb_color_t::none() -{ - return rgb_color_t(type_none); -} +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::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::black() { return rgb_color_t(type_named, 0); } -static unsigned char term8_color_for_rgb(const unsigned char rgb[3]) -{ - const uint32_t kColors[] = - { - 0x000000, //Black - 0xFF0000, //Red - 0x00FF00, //Green - 0xFFFF00, //Yellow - 0x0000FF, //Blue - 0xFF00FF, //Magenta - 0x00FFFF, //Cyan - 0xFFFFFF, //White +static unsigned char term8_color_for_rgb(const unsigned char rgb[3]) { + const uint32_t kColors[] = { + 0x000000, // Black + 0xFF0000, // Red + 0x00FF00, // Green + 0xFFFF00, // Yellow + 0x0000FF, // Blue + 0xFF00FF, // Magenta + 0x00FFFF, // Cyan + 0xFFFFFF, // White }; 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] = - { - 0x000000, 0x00005f, 0x000087, 0x0000af, 0x0000d7, 0x0000ff, 0x005f00, 0x005f5f, - 0x005f87, 0x005faf, 0x005fd7, 0x005fff, 0x008700, 0x00875f, 0x008787, 0x0087af, - 0x0087d7, 0x0087ff, 0x00af00, 0x00af5f, 0x00af87, 0x00afaf, 0x00afd7, 0x00afff, - 0x00d700, 0x00d75f, 0x00d787, 0x00d7af, 0x00d7d7, 0x00d7ff, 0x00ff00, 0x00ff5f, - 0x00ff87, 0x00ffaf, 0x00ffd7, 0x00ffff, 0x5f0000, 0x5f005f, 0x5f0087, 0x5f00af, - 0x5f00d7, 0x5f00ff, 0x5f5f00, 0x5f5f5f, 0x5f5f87, 0x5f5faf, 0x5f5fd7, 0x5f5fff, - 0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af, 0x5f87d7, 0x5f87ff, 0x5faf00, 0x5faf5f, - 0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff, 0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af, - 0x5fd7d7, 0x5fd7ff, 0x5fff00, 0x5fff5f, 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff, - 0x870000, 0x87005f, 0x870087, 0x8700af, 0x8700d7, 0x8700ff, 0x875f00, 0x875f5f, - 0x875f87, 0x875faf, 0x875fd7, 0x875fff, 0x878700, 0x87875f, 0x878787, 0x8787af, - 0x8787d7, 0x8787ff, 0x87af00, 0x87af5f, 0x87af87, 0x87afaf, 0x87afd7, 0x87afff, - 0x87d700, 0x87d75f, 0x87d787, 0x87d7af, 0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f, - 0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, 0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af, - 0xaf00d7, 0xaf00ff, 0xaf5f00, 0xaf5f5f, 0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff, - 0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af, 0xaf87d7, 0xaf87ff, 0xafaf00, 0xafaf5f, - 0xafaf87, 0xafafaf, 0xafafd7, 0xafafff, 0xafd700, 0xafd75f, 0xafd787, 0xafd7af, - 0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f, 0xafff87, 0xafffaf, 0xafffd7, 0xafffff, - 0xd70000, 0xd7005f, 0xd70087, 0xd700af, 0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f, - 0xd75f87, 0xd75faf, 0xd75fd7, 0xd75fff, 0xd78700, 0xd7875f, 0xd78787, 0xd787af, - 0xd787d7, 0xd787ff, 0xd7af00, 0xd7af5f, 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff, - 0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af, 0xd7d7d7, 0xd7d7ff, 0xd7ff00, 0xd7ff5f, - 0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff, 0xff0000, 0xff005f, 0xff0087, 0xff00af, - 0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f, 0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff, - 0xff8700, 0xff875f, 0xff8787, 0xff87af, 0xff87d7, 0xff87ff, 0xffaf00, 0xffaf5f, - 0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, 0xffd700, 0xffd75f, 0xffd787, 0xffd7af, - 0xffd7d7, 0xffd7ff, 0xffff00, 0xffff5f, 0xffff87, 0xffffaf, 0xffffd7, 0xffffff, - 0x080808, 0x121212, 0x1c1c1c, 0x262626, 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e, - 0x585858, 0x626262, 0x6c6c6c, 0x767676, 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e, - 0xa8a8a8, 0xb2b2b2, 0xbcbcbc, 0xc6c6c6, 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee - }; +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, 0x00d700, 0x00d75f, 0x00d787, + 0x00d7af, 0x00d7d7, 0x00d7ff, 0x00ff00, 0x00ff5f, 0x00ff87, 0x00ffaf, 0x00ffd7, 0x00ffff, + 0x5f0000, 0x5f005f, 0x5f0087, 0x5f00af, 0x5f00d7, 0x5f00ff, 0x5f5f00, 0x5f5f5f, 0x5f5f87, + 0x5f5faf, 0x5f5fd7, 0x5f5fff, 0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af, 0x5f87d7, 0x5f87ff, + 0x5faf00, 0x5faf5f, 0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff, 0x5fd700, 0x5fd75f, 0x5fd787, + 0x5fd7af, 0x5fd7d7, 0x5fd7ff, 0x5fff00, 0x5fff5f, 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff, + 0x870000, 0x87005f, 0x870087, 0x8700af, 0x8700d7, 0x8700ff, 0x875f00, 0x875f5f, 0x875f87, + 0x875faf, 0x875fd7, 0x875fff, 0x878700, 0x87875f, 0x878787, 0x8787af, 0x8787d7, 0x8787ff, + 0x87af00, 0x87af5f, 0x87af87, 0x87afaf, 0x87afd7, 0x87afff, 0x87d700, 0x87d75f, 0x87d787, + 0x87d7af, 0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f, 0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, + 0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af, 0xaf00d7, 0xaf00ff, 0xaf5f00, 0xaf5f5f, 0xaf5f87, + 0xaf5faf, 0xaf5fd7, 0xaf5fff, 0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af, 0xaf87d7, 0xaf87ff, + 0xafaf00, 0xafaf5f, 0xafaf87, 0xafafaf, 0xafafd7, 0xafafff, 0xafd700, 0xafd75f, 0xafd787, + 0xafd7af, 0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f, 0xafff87, 0xafffaf, 0xafffd7, 0xafffff, + 0xd70000, 0xd7005f, 0xd70087, 0xd700af, 0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f, 0xd75f87, + 0xd75faf, 0xd75fd7, 0xd75fff, 0xd78700, 0xd7875f, 0xd78787, 0xd787af, 0xd787d7, 0xd787ff, + 0xd7af00, 0xd7af5f, 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff, 0xd7d700, 0xd7d75f, 0xd7d787, + 0xd7d7af, 0xd7d7d7, 0xd7d7ff, 0xd7ff00, 0xd7ff5f, 0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff, + 0xff0000, 0xff005f, 0xff0087, 0xff00af, 0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f, 0xff5f87, + 0xff5faf, 0xff5fd7, 0xff5fff, 0xff8700, 0xff875f, 0xff8787, 0xff87af, 0xff87d7, 0xff87ff, + 0xffaf00, 0xffaf5f, 0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, 0xffd700, 0xffd75f, 0xffd787, + 0xffd7af, 0xffd7d7, 0xffd7ff, 0xffff00, 0xffff5f, 0xffff87, 0xffffaf, 0xffffd7, 0xffffff, + 0x080808, 0x121212, 0x1c1c1c, 0x262626, 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e, 0x585858, + 0x626262, 0x6c6c6c, 0x767676, 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e, 0xa8a8a8, 0xb2b2b2, + 0xbcbcbc, 0xc6c6c6, 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee}; 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.color.rgb); } -color24_t rgb_color_t::to_color24() const -{ +color24_t rgb_color_t::to_color24() const { assert(type == type_rgb); return data.color; } -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.color.rgb); - } - else - { - /* This is an error */ - return (unsigned char)(-1); + } else { + return (unsigned char)(-1); // this is an error } } -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) success = try_parse_special(str); + if (!success) success = try_parse_named(str); + if (!success) success = try_parse_rgb(str); + if (!success) { memset(&this->data, 0, sizeof this->data); this->type = type_none; } } -rgb_color_t::rgb_color_t(const wcstring &str) : type(), flags() -{ - this->parse(str); -} +rgb_color_t::rgb_color_t(const wcstring &str) : type(), flags() { this->parse(str); } -rgb_color_t::rgb_color_t(const std::string &str) : type(), flags() -{ +rgb_color_t::rgb_color_t(const std::string &str) : type(), flags() { this->parse(str2wcstring(str)); } -wcstring rgb_color_t::description() const -{ - switch (type) - { +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)); + 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.color.rgb[0], data.color.rgb[1], data.color.rgb[2]); + return format_string(L"rgb(0x%02x%02x%02x)", data.color.rgb[0], data.color.rgb[1], + data.color.rgb[2]); case type_reset: return L"reset"; case type_normal: diff --git a/src/color.h b/src/color.h index 881ce4b38..2466e9e88 100644 --- a/src/color.h +++ b/src/color.h @@ -2,173 +2,131 @@ #ifndef FISH_COLOR_H #define FISH_COLOR_H +#include #include #include -#include #include "common.h" -/* 24 bit color */ -struct color24_t -{ +// 24-bit color. +struct color24_t { unsigned char rgb[3]; }; -/* A type that represents a color. We work hard to keep it at a size of 4 bytes. */ -class rgb_color_t -{ +/// A type that represents a color. We work hard to keep it at a size of 4 bytes. +class rgb_color_t { + // Types + enum { type_none, type_named, type_rgb, type_normal, type_reset }; + unsigned char type : 4; - /* Types */ - enum - { - type_none, - type_named, - type_rgb, - type_normal, - type_reset - }; - unsigned char type:4; + // Flags + enum { flag_bold = 1 << 0, flag_underline = 1 << 1 }; + unsigned char flags : 4; - /* Flags */ - enum - { - flag_bold = 1 << 0, - flag_underline = 1 << 1 - }; - unsigned char flags:4; - - union - { - unsigned char name_idx; //0-10 + union { + unsigned char name_idx; // 0-10 color24_t color; } data; - /** Try parsing a special color name like "normal" */ + /// Try parsing a special color name like "normal". bool try_parse_special(const wcstring &str); - /** Try parsing an rgb color like "#F0A030" */ + /// Try parsing an rgb color like "#F0A030". bool try_parse_rgb(const wcstring &str); - /** Try parsing an explicit color name like "magenta" */ + /// Try parsing an explicit color name like "magenta". bool try_parse_named(const wcstring &str); - /* Parsing entry point */ + /// Parsing entry point. void parse(const wcstring &str); - /** Private constructor */ - explicit rgb_color_t(unsigned char t, unsigned char i=0); + /// Private constructor. + explicit rgb_color_t(unsigned char t, unsigned char i = 0); -public: - - /** Default constructor of type none */ + public: + /// Default constructor of type none. explicit rgb_color_t() : type(type_none), flags(), data() {} - /** Parse a color from a string */ + /// Parse a color from a string. explicit rgb_color_t(const wcstring &str); explicit rgb_color_t(const std::string &str); - /** Returns white */ + /// Returns white. static rgb_color_t white(); - /** Returns black */ + /// Returns black. static rgb_color_t black(); - /** Returns the reset special color */ + /// Returns the reset special color. static rgb_color_t reset(); - /** Returns the normal special color */ + /// Returns the normal special color. static rgb_color_t normal(); - /** Returns the none special color */ + /// Returns the none special color. static rgb_color_t none(); - /** Returns whether the color is the normal special color */ - bool is_normal(void) const - { - return type == type_normal; - } + /// Returns whether the color is the normal special color. + bool is_normal(void) const { return type == type_normal; } - /** Returns whether the color is the reset special color */ - bool is_reset(void) const - { - return type == type_reset; - } + /// Returns whether the color is the reset special color. + bool is_reset(void) const { return type == type_reset; } - /** Returns whether the color is the none special color */ - bool is_none(void) const - { - return type == type_none; - } + /// Returns whether the color is the none special color. + bool is_none(void) const { return type == type_none; } - /** Returns whether the color is a named color (like "magenta") */ - bool is_named(void) const - { - return type == type_named; - } + /// Returns whether the color is a named color (like "magenta"). + bool is_named(void) const { return type == type_named; } - /** Returns whether the color is specified via RGB components */ - bool is_rgb(void) const - { - return type == type_rgb; - } + /// Returns whether the color is specified via RGB components. + bool is_rgb(void) const { return type == type_rgb; } - /** Returns whether the color is special, that is, not rgb or named */ - bool is_special(void) const - { - return type != type_named && type != type_rgb; - } + /// Returns whether the color is special, that is, not rgb or named. + bool is_special(void) const { return type != type_named && type != type_rgb; } - /** Returns a description of the color */ + /// Returns a description of the color. wcstring description() const; - /** Returns the name index for the given color. Requires that the color be named or RGB. */ + /// Returns the name index for the given color. Requires that the color be named or RGB. unsigned char to_name_index() const; - /** Returns the term256 index for the given color. Requires that the color be RGB. */ + /// Returns the term256 index for the given color. Requires that the color be RGB. unsigned char to_term256_index() const; - /** Returns the 24 bit color for the given color. Requires that the color be RGB. */ + /// Returns the 24 bit color for the given color. Requires that the color be RGB. color24_t to_color24() const; - /** Returns whether the color is bold */ - bool is_bold() const - { - return !!(flags & flag_bold); + /// Returns whether the color is bold. + bool is_bold() const { return !!(flags & flag_bold); } + + /// Set whether the color is bold. + void set_bold(bool x) { + if (x) + flags |= flag_bold; + else + flags &= ~flag_bold; } - /** Set whether the color is bold */ - void set_bold(bool x) - { - if (x) flags |= flag_bold; - else flags &= ~flag_bold; + /// Returns whether the color is underlined. + bool is_underline() const { return !!(flags & flag_underline); } + + /// Set whether the color is underlined. + void set_underline(bool x) { + if (x) + flags |= flag_underline; + else + flags &= ~flag_underline; } - /** Returns whether the color is underlined */ - bool is_underline() const - { - return !!(flags & flag_underline); + /// Compare two colors for equality. + bool operator==(const rgb_color_t &other) const { + return type == other.type && !memcmp(&data, &other.data, sizeof data); } - /** Set whether the color is underlined */ - void set_underline(bool x) - { - if (x) flags |= flag_underline; - else flags &= ~flag_underline; - } + /// Compare two colors for inequality. + bool operator!=(const rgb_color_t &other) const { return !(*this == other); } - /** Compare two colors for equality */ - bool operator==(const rgb_color_t &other) const - { - return type == other.type && ! memcmp(&data, &other.data, sizeof data); - } - - /** Compare two colors for inequality */ - bool operator!=(const rgb_color_t &other) const - { - return !(*this == other); - } - - /** Returns the names of all named colors */ + /// Returns the names of all named colors. static wcstring_list_t named_color_names(void); }; From e08039a2bd0352b219c733140365ec0c8ef9cca6 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Wed, 27 Apr 2016 16:10:14 -0700 Subject: [PATCH 147/363] restyle common module to match project style Reduces lint errors from 194 to 142 (-27%). Line count from 3352 to 2645 (-21%). Another step in resolving issue #2902. --- src/common.cpp | 1972 ++++++++++++++++++------------------------------ src/common.h | 877 +++++++++------------ 2 files changed, 1072 insertions(+), 1777 deletions(-) diff --git a/src/common.cpp b/src/common.cpp index 75f3c945a..eedcd1c99 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -1,46 +1,43 @@ -/** \file common.c - -Various functions, mostly string utilities, that are used by most -parts of fish. -*/ +// Various functions, mostly string utilities, that are used by most parts of fish. #include "config.h" -#include -#ifdef HAVE_SIGINFO_H -#include -#endif -#include -#include -#include -#include -#include -#include #include -#include -#include -#ifdef HAVE_SYS_IOCTL_H -#include -#endif -#include -#include #include #include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include +#include +#include +#include +#include +#include #ifdef HAVE_EXECINFO_H #include #endif -#include -#include +#ifdef HAVE_SIGINFO_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#include +#include // IWYU pragma: keep -#include "fallback.h" // IWYU pragma: keep -#include "wutil.h" // IWYU pragma: keep #include "common.h" #include "expand.h" +#include "fallback.h" // IWYU pragma: keep #include "wildcard.h" +#include "wutil.h" // IWYU pragma: keep #define NOT_A_WCHAR (static_cast(WEOF)) @@ -57,25 +54,22 @@ bool g_profiling_active = false; const wchar_t *program_name; -int debug_level=1; +int debug_level = 1; -/** - This struct maintains the current state of the terminal size. It is updated on demand after receiving a SIGWINCH. - Do not touch this struct directly, it's managed with a rwlock. Use common_get_width()/common_get_height(). -*/ +/// This struct maintains the current state of the terminal size. It is updated on demand after +/// receiving a SIGWINCH. Do not touch this struct directly, it's managed with a rwlock. Use +/// common_get_width()/common_get_height(). static struct winsize termsize; static volatile bool termsize_valid; static rwlock_t termsize_rwlock; static char *wcs2str_internal(const wchar_t *in, char *out); -void show_stackframe() -{ +void show_stackframe() { ASSERT_IS_NOT_FORKED_CHILD(); - /* Hack to avoid showing backtraces in the tester */ - if (program_name && ! wcscmp(program_name, L"(ignore)")) - return; + // Hack to avoid showing backtraces in the tester. + if (program_name && !wcscmp(program_name, L"(ignore)")) return; void *trace[32]; int trace_size = 0; @@ -85,65 +79,55 @@ void show_stackframe() backtrace_symbols_fd(trace, trace_size, STDERR_FILENO); } -int fgetws2(wcstring *s, FILE *f) -{ - int i=0; +int fgetws2(wcstring *s, FILE *f) { + int i = 0; wint_t c; - while (1) - { - errno=0; + while (1) { + errno = 0; c = fgetwc(f); - if (errno == EILSEQ || errno == EINTR) - { + if (errno == EILSEQ || errno == EINTR) { continue; } - switch (c) - { - /* End of line */ + switch (c) { + // End of line. case WEOF: case L'\n': - case L'\0': + case L'\0': { return i; - /* Ignore carriage returns */ - case L'\r': + } + // Ignore carriage returns. + case L'\r': { break; - - default: + } + default: { i++; s->push_back((wchar_t)c); break; + } } } } -/** - Converts the narrow character string \c in into its wide - equivalent, and return it - - The string may contain embedded nulls. - - This function encodes illegal character sequences in a reversible - way using the private use area. -*/ - -static wcstring str2wcs_internal(const char *in, const size_t in_len) -{ - if (in_len == 0) - return wcstring(); - +/// Converts the narrow character string \c in into its wide equivalent, and return it. +/// +/// The string may contain embedded nulls. +/// +/// This function encodes illegal character sequences in a reversible way using the private use +/// area. +static wcstring str2wcs_internal(const char *in, const size_t in_len) { + if (in_len == 0) return wcstring(); assert(in != NULL); wcstring result; result.reserve(in_len); size_t in_pos = 0; - if (MB_CUR_MAX == 1) // single-byte locale, all values are legal + if (MB_CUR_MAX == 1) // single-byte locale, all values are legal { - while (in_pos < in_len) - { + while (in_pos < in_len) { result.push_back((unsigned char)in[in_pos]); in_pos++; } @@ -151,54 +135,39 @@ static wcstring str2wcs_internal(const char *in, const size_t in_len) } mbstate_t state = {}; - while (in_pos < in_len) - { + while (in_pos < in_len) { wchar_t wc = 0; - size_t ret = mbrtowc(&wc, &in[in_pos], in_len-in_pos, &state); + size_t ret = mbrtowc(&wc, &in[in_pos], in_len - in_pos, &state); - /* Determine whether to encode this characters with our crazy scheme */ + // Determine whether to encode this characters with our crazy scheme. bool use_encode_direct = false; - if (wc >= ENCODE_DIRECT_BASE && wc < ENCODE_DIRECT_BASE+256) - { + if (wc >= ENCODE_DIRECT_BASE && wc < ENCODE_DIRECT_BASE + 256) { use_encode_direct = true; - } - else if (wc == INTERNAL_SEPARATOR) - { + } else if (wc == INTERNAL_SEPARATOR) { use_encode_direct = true; - } - else if (ret == (size_t)-2) - { - /* Incomplete sequence */ + } else if (ret == (size_t)-2) { + // Incomplete sequence. use_encode_direct = true; - } - else if (ret == (size_t)-1) - { - /* Invalid data */ + } else if (ret == (size_t)-1) { + // Invalid data. use_encode_direct = true; - } - else if (ret > in_len - in_pos) - { - /* Other error codes? Terrifying, should never happen */ + } else if (ret > in_len - in_pos) { + // Other error codes? Terrifying, should never happen. use_encode_direct = true; } - if (use_encode_direct) - { + if (use_encode_direct) { wc = ENCODE_DIRECT_BASE + (unsigned char)in[in_pos]; result.push_back(wc); in_pos++; memset(&state, 0, sizeof state); - } - else if (ret == 0) - { - /* Embedded null byte! */ + } else if (ret == 0) { + // Embedded null byte! result.push_back(L'\0'); in_pos++; memset(&state, 0, sizeof state); - } - else - { - /* Normal case */ + } else { + // Normal case. result.push_back(wc); in_pos += ret; } @@ -206,102 +175,72 @@ static wcstring str2wcs_internal(const char *in, const size_t in_len) return result; } -wcstring str2wcstring(const char *in, size_t len) -{ - return str2wcs_internal(in, len); -} +wcstring str2wcstring(const char *in, size_t len) { return str2wcs_internal(in, len); } -wcstring str2wcstring(const char *in) -{ - return str2wcs_internal(in, strlen(in)); -} +wcstring str2wcstring(const char *in) { return str2wcs_internal(in, strlen(in)); } -wcstring str2wcstring(const std::string &in) -{ +wcstring str2wcstring(const std::string &in) { /* Handles embedded nulls! */ return str2wcs_internal(in.data(), in.size()); } -char *wcs2str(const wchar_t *in) -{ - if (! in) - return NULL; - size_t desired_size = MAX_UTF8_BYTES*wcslen(in)+1; +char *wcs2str(const wchar_t *in) { + if (!in) return NULL; + size_t desired_size = MAX_UTF8_BYTES * wcslen(in) + 1; char local_buff[512]; - if (desired_size <= sizeof local_buff / sizeof *local_buff) - { - // convert into local buff, then use strdup() so we don't waste malloc'd space + 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) - { - // It converted into the local buffer, so copy it + if (result) { + // It converted into the local buffer, so copy it. result = strdup(result); - if (! result) - { + if (!result) { DIE_MEM(); } } return result; - } - else - { - // here we fall into the bad case of allocating a buffer probably much larger than necessary - char *out = (char *)malloc(MAX_UTF8_BYTES*wcslen(in)+1); - if (!out) - { + } else { + // Here we probably allocate a buffer probably much larger than necessary. + char *out = (char *)malloc(MAX_UTF8_BYTES * wcslen(in) + 1); + if (!out) { DIE_MEM(); } return wcs2str_internal(in, out); } } -char *wcs2str(const wcstring &in) -{ - return wcs2str(in.c_str()); -} +char *wcs2str(const wcstring &in) { return wcs2str(in.c_str()); } -/* This function is distinguished from wcs2str_internal in that it allows embedded null bytes */ -std::string wcs2string(const wcstring &input) -{ +/// This function is distinguished from wcs2str_internal in that it allows embedded null bytes. +std::string wcs2string(const wcstring &input) { std::string result; result.reserve(input.size()); mbstate_t state = {}; char converted[MB_LEN_MAX + 1]; - for (size_t i=0; i < input.size(); i++) - { + for (size_t i = 0; i < input.size(); i++) { wchar_t wc = input[i]; - if (wc == INTERNAL_SEPARATOR) - { + if (wc == INTERNAL_SEPARATOR) { // Do nothing. - } - else if (wc >= ENCODE_DIRECT_BASE && wc < ENCODE_DIRECT_BASE + 256) - { + } else if (wc >= ENCODE_DIRECT_BASE && wc < ENCODE_DIRECT_BASE + 256) { result.push_back(wc - ENCODE_DIRECT_BASE); - } - else if (MB_CUR_MAX == 1) // single-byte locale (C/POSIX/ISO-8859) + } else if (MB_CUR_MAX == 1) // single-byte locale (C/POSIX/ISO-8859) { // If `wc` contains a wide character we emit a question-mark. - if (wc & ~0xFF) - { + if (wc & ~0xFF) { wc = '?'; } converted[0] = wc; result.append(converted, 1); - } - else - { + } else { memset(converted, 0, sizeof converted); size_t len = wcrtomb(converted, wc, &state); - if (len == (size_t)(-1)) - { + if (len == (size_t)(-1)) { debug(1, L"Wide character %d has no narrow representation", wc); memset(&state, 0, sizeof(state)); - } - else - { + } else { result.append(converted, len); } } @@ -310,16 +249,12 @@ std::string wcs2string(const wcstring &input) return result; } -/** - Converts the wide character string \c in into it's narrow - equivalent, stored in \c out. \c out must have enough space to fit - the entire string. - - This function decodes illegal character sequences in a reversible - way using the private use area. -*/ -static char *wcs2str_internal(const wchar_t *in, char *out) -{ +/// Converts the wide character string \c in into it's narrow equivalent, stored in \c out. \c out +/// must have enough space to fit the entire string. +/// +/// This function decodes illegal character sequences in a reversible way using the private use +/// area. +static char *wcs2str_internal(const wchar_t *in, char *out) { CHECK(in, 0); CHECK(out, 0); @@ -327,39 +262,25 @@ static char *wcs2str_internal(const wchar_t *in, char *out) size_t out_pos = 0; mbstate_t state = {}; - while (in[in_pos]) - { - if (in[in_pos] == INTERNAL_SEPARATOR) - { + while (in[in_pos]) { + if (in[in_pos] == INTERNAL_SEPARATOR) { // Do nothing. - } - 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 if (MB_CUR_MAX == 1) // single-byte locale (C/POSIX/ISO-8859) + } 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 if (MB_CUR_MAX == 1) // single-byte locale (C/POSIX/ISO-8859) { // If `wc` contains a wide character we emit a question-mark. - if (in[in_pos] & ~0xFF) - { + if (in[in_pos] & ~0xFF) { out[out_pos++] = '?'; - } - else - { + } else { out[out_pos++] = (unsigned char)in[in_pos]; } - } - else - { + } else { size_t len = wcrtomb(&out[out_pos], in[in_pos], &state); - if (len == (size_t)-1) - { + if (len == (size_t)-1) { debug(1, L"Wide character %d has no narrow representation", in[in_pos]); memset(&state, 0, sizeof(state)); - } - else - { + } else { out_pos += len; } } @@ -370,8 +291,7 @@ static char *wcs2str_internal(const wchar_t *in, char *out) return out; } -wcstring format_string(const wchar_t *format, ...) -{ +wcstring format_string(const wchar_t *format, ...) { va_list va; va_start(va, format); wcstring result = vformat_string(format, va); @@ -379,51 +299,39 @@ wcstring format_string(const wchar_t *format, ...) return result; } -void append_formatv(wcstring &target, const wchar_t *format, va_list va_orig) -{ +void append_formatv(wcstring &target, const wchar_t *format, va_list va_orig) { const int saved_err = errno; - /* - As far as I know, there is no way to check if a - vswprintf-call failed because of a badly formated string - option or because the supplied destination string was to - small. In GLIBC, errno seems to be set to EINVAL either way. - - Because of this, on failiure we try to - increase the buffer size until the free space is - larger than max_size, at which point it will - conclude that the error was probably due to a badly - formated string option, and return an error. Make - sure to null terminate string before that, though. - */ - const size_t max_size = (128*1024*1024); + // As far as I know, there is no way to check if a vswprintf-call failed because of a badly + // formated string option or because the supplied destination string was to small. In GLIBC, + // errno seems to be set to EINVAL either way. + // + // Because of this, on failiure we try to increase the buffer size until the free space is + // larger than max_size, at which point it will conclude that the error was probably due to a + // badly formated string option, and return an error. Make sure to null terminate string before + // that, though. + const size_t max_size = (128 * 1024 * 1024); wchar_t static_buff[256]; size_t size = 0; wchar_t *buff = NULL; int status = -1; - while (status < 0) - { - /* Reallocate if necessary */ - if (size == 0) - { + while (status < 0) { + // Reallocate if necessary. + if (size == 0) { buff = static_buff; size = sizeof static_buff; - } - else - { + } else { size *= 2; - if (size >= max_size) - { + if (size >= max_size) { buff[0] = '\0'; break; } buff = (wchar_t *)realloc((buff == static_buff ? NULL : buff), size); - if (buff == NULL) - { + if (buff == NULL) { DIE_MEM(); } } - /* Try printing */ + // Try printing. va_list va; va_copy(va, va_orig); status = vswprintf(buff, size / sizeof(wchar_t), format, va); @@ -432,35 +340,29 @@ void append_formatv(wcstring &target, const wchar_t *format, va_list va_orig) target.append(buff); - if (buff != static_buff) - { + if (buff != static_buff) { free(buff); } errno = saved_err; } -wcstring vformat_string(const wchar_t *format, va_list va_orig) -{ +wcstring vformat_string(const wchar_t *format, va_list va_orig) { wcstring result; append_formatv(result, format, va_orig); return result; } -void append_format(wcstring &str, const wchar_t *format, ...) -{ +void append_format(wcstring &str, const wchar_t *format, ...) { va_list va; va_start(va, format); append_formatv(str, format, va); va_end(va); } -const wchar_t *wcsvarname(const wchar_t *str) -{ - while (*str) - { - if ((!iswalnum(*str)) && (*str != L'_')) - { +const wchar_t *wcsvarname(const wchar_t *str) { + while (*str) { + if ((!iswalnum(*str)) && (*str != L'_')) { return str; } str++; @@ -468,72 +370,42 @@ const wchar_t *wcsvarname(const wchar_t *str) return NULL; } -const wchar_t *wcsvarname(const wcstring &str) -{ - return wcsvarname(str.c_str()); -} +const wchar_t *wcsvarname(const wcstring &str) { return wcsvarname(str.c_str()); } -const wchar_t *wcsfuncname(const wcstring &str) -{ - return wcschr(str.c_str(), L'/'); -} +const wchar_t *wcsfuncname(const wcstring &str) { return wcschr(str.c_str(), L'/'); } +bool wcsvarchr(wchar_t chr) { return iswalnum(chr) || chr == L'_'; } -bool wcsvarchr(wchar_t chr) -{ - return iswalnum(chr) || chr == L'_'; -} +int fish_wcswidth(const wchar_t *str) { return fish_wcswidth(str, wcslen(str)); } -int fish_wcswidth(const wchar_t *str) -{ - return fish_wcswidth(str, wcslen(str)); -} +int fish_wcswidth(const wcstring &str) { return fish_wcswidth(str.c_str(), str.size()); } -int fish_wcswidth(const wcstring& str) -{ - return fish_wcswidth(str.c_str(), str.size()); -} - -wchar_t *quote_end(const wchar_t *pos) -{ +wchar_t *quote_end(const wchar_t *pos) { wchar_t c = *pos; - while (1) - { + while (1) { pos++; - if (!*pos) - return 0; + if (!*pos) return 0; - if (*pos == L'\\') - { + if (*pos == L'\\') { pos++; - if (!*pos) - return 0; - } - else - { - if (*pos == c) - { + if (!*pos) return 0; + } else { + if (*pos == c) { return (wchar_t *)pos; } } } return 0; - } - -wcstring wsetlocale(int category, const wchar_t *locale) -{ - +wcstring wsetlocale(int category, const wchar_t *locale) { char *lang = locale ? wcs2str(locale) : NULL; char *res = setlocale(category, lang); free(lang); - /* - Use ellipsis if on known unicode system, otherwise use $ - */ + // Use ellipsis if on known unicode system, otherwise use $. ellipsis_char = (wcwidth(L'\x2026') > 0) ? L'\x2026' : L'$'; // U+23CE is the "return" character @@ -545,8 +417,7 @@ wcstring wsetlocale(int category, const wchar_t *locale) return format_string(L"%s", res); } -bool contains_internal(const wchar_t *a, int vararg_handle, ...) -{ +bool contains_internal(const wchar_t *a, int vararg_handle, ...) { const wchar_t *arg; va_list va; bool res = false; @@ -554,44 +425,38 @@ bool contains_internal(const wchar_t *a, int vararg_handle, ...) CHECK(a, 0); va_start(va, vararg_handle); - while ((arg=va_arg(va, const wchar_t *))!= 0) - { - if (wcscmp(a,arg) == 0) - { + 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 *. vararg_handle exists only to give us a POD-value to pass to va_start */ -__sentinel bool contains_internal(const wcstring &needle, int vararg_handle, ...) -{ +/// wcstring variant of contains_internal. The first parameter is a wcstring, the rest are const +/// wchar_t *. vararg_handle exists only to give us a POD-value to pass to va_start. +__sentinel bool contains_internal(const wcstring &needle, int vararg_handle, ...) { const wchar_t *arg; va_list va; int res = 0; const wchar_t *needle_cstr = needle.c_str(); va_start(va, vararg_handle); - while ((arg=va_arg(va, const wchar_t *))!= 0) - { - /* libc++ has an unfortunate implementation of operator== that unconditonally wcslen's the wchar_t* parameter, so prefer wcscmp directly */ - if (! wcscmp(needle_cstr, arg)) - { - res=1; + while ((arg = va_arg(va, const wchar_t *)) != 0) { + // libc++ has an unfortunate implementation of operator== that unconditonally wcslen's the + // wchar_t* parameter, so prefer wcscmp directly. + if (!wcscmp(needle_cstr, arg)) { + res = 1; break; } - } va_end(va); return res; } -long read_blocked(int fd, void *buf, size_t count) -{ +long read_blocked(int fd, void *buf, size_t count) { ssize_t res; sigset_t chldset, oldset; @@ -603,60 +468,45 @@ long read_blocked(int fd, void *buf, size_t count) return res; } -ssize_t write_loop(int fd, const char *buff, size_t count) -{ - size_t out_cum=0; - while (out_cum < count) - { +ssize_t write_loop(int fd, const char *buff, size_t count) { + size_t out_cum = 0; + while (out_cum < count) { ssize_t out = write(fd, &buff[out_cum], count - out_cum); - if (out < 0) - { - if (errno != EAGAIN && errno != EINTR) - { + if (out < 0) { + if (errno != EAGAIN && errno != EINTR) { return -1; } - } - else - { + } else { out_cum += (size_t)out; } } return (ssize_t)out_cum; } -ssize_t read_loop(int fd, void *buff, size_t count) -{ +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; +static bool should_debug(int level) { + if (level > debug_level) return false; - /* Hack to not print error messages in the tests */ - if (program_name && ! wcscmp(program_name, L"(ignore)")) - return false; + // Hack to not print error messages in the tests. + if (program_name && !wcscmp(program_name, L"(ignore)")) return false; return true; } -static void debug_shared(const wcstring &msg) -{ +static void debug_shared(const wcstring &msg) { const wcstring sb = wcstring(program_name) + L": " + msg; fwprintf(stderr, L"%ls", reformat_for_screen(sb).c_str()); } -void debug(int level, const wchar_t *msg, ...) -{ - if (! should_debug(level)) - return; +void debug(int level, const wchar_t *msg, ...) { + if (!should_debug(level)) return; int errno_old = errno; va_list va; va_start(va, msg); @@ -666,10 +516,8 @@ void debug(int level, const wchar_t *msg, ...) errno = errno_old; } -void debug(int level, const char *msg, ...) -{ - if (! should_debug(level)) - return; +void debug(int level, const char *msg, ...) { + if (!should_debug(level)) return; int errno_old = errno; char local_msg[512]; va_list va; @@ -680,95 +528,78 @@ void debug(int level, const char *msg, ...) errno = errno_old; } -void read_ignore(int fd, void *buff, size_t count) -{ +void read_ignore(int fd, void *buff, size_t count) { size_t ignore __attribute__((unused)); ignore = read(fd, buff, count); } -void write_ignore(int fd, const void *buff, size_t count) -{ +void write_ignore(int fd, const void *buff, size_t count) { size_t ignore __attribute__((unused)); ignore = write(fd, buff, count); } +void debug_safe(int level, const char *msg, const char *param1, const char *param2, + const char *param3, const char *param4, const char *param5, const char *param6, + const char *param7, const char *param8, const char *param9, const char *param10, + const char *param11, const char *param12) { + const char *const params[] = {param1, param2, param3, param4, param5, param6, + param7, param8, param9, param10, param11, param12}; + if (!msg) return; -void debug_safe(int level, const char *msg, const char *param1, const char *param2, const char *param3, const char *param4, const char *param5, const char *param6, const char *param7, const char *param8, const char *param9, const char *param10, const char *param11, const char *param12) -{ - 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; + // 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); + if (end == NULL) end = cursor + strlen(cursor); write_ignore(STDERR_FILENO, cursor, end - cursor); - if (end[0] == '%' && end[1] == 's') - { - /* Handle a format string */ + 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) - format = "(null)"; + if (!format) format = "(null)"; write_ignore(STDERR_FILENO, format, strlen(format)); cursor = end + 2; - } - else if (end[0] == '\0') - { - /* Must be at the end of the string */ + } else if (end[0] == '\0') { + // Must be at the end of the string. cursor = end; - } - else - { - /* Some other format specifier, just skip it */ + } else { + // Some other format specifier, just skip it. cursor = end + 1; } } - // We always append a newline + // We always append a newline. write_ignore(STDERR_FILENO, "\n", 1); errno = errno_old; } -void format_long_safe(char buff[64], long val) -{ - if (val == 0) - { +void format_long_safe(char buff[64], long val) { + if (val == 0) { strcpy(buff, "0"); - } - else - { - /* Generate the string in reverse */ + } 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) - { + // 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) { long rem = val % 10; buff[idx++] = '0' + (rem < 0 ? -rem : rem); val /= 10; } - if (negative) - buff[idx++] = '-'; + 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; @@ -776,31 +607,24 @@ void format_long_safe(char buff[64], long val) } } -void format_long_safe(wchar_t buff[64], long val) -{ - if (val == 0) - { +void format_long_safe(wchar_t buff[64], long val) { + if (val == 0) { wcscpy(buff, L"0"); - } - else - { - /* Generate the string in reverse */ + } else { + // Generate the string in reverse. size_t idx = 0; bool negative = (val < 0); - while (val != 0) - { + while (val != 0) { long rem = val % 10; buff[idx++] = L'0' + (wchar_t)(rem < 0 ? -rem : rem); val /= 10; } - if (negative) - buff[idx++] = L'-'; + 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; @@ -808,17 +632,13 @@ void format_long_safe(wchar_t buff[64], long val) } } -void narrow_string_safe(char buff[64], const wchar_t *s) -{ +void narrow_string_safe(char buff[64], const wchar_t *s) { size_t idx = 0; - for (size_t widx = 0; s[widx] != L'\0'; widx++) - { + for (size_t widx = 0; s[widx] != L'\0'; widx++) { wchar_t c = s[widx]; - if (c <= 127) - { + if (c <= 127) { buff[idx++] = char(c); - if (idx + 1 == 64) - { + if (idx + 1 == 64) { break; } } @@ -826,34 +646,24 @@ void narrow_string_safe(char buff[64], const wchar_t *s) buff[idx] = '\0'; } -wcstring reformat_for_screen(const wcstring &msg) -{ +wcstring reformat_for_screen(const wcstring &msg) { wcstring buff; int line_width = 0; int screen_width = common_get_width(); - if (screen_width) - { + if (screen_width) { const wchar_t *start = msg.c_str(); const wchar_t *pos = start; - while (1) - { + while (1) { int overflow = 0; - int tok_width=0; + int 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)) - { + // 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; } @@ -862,163 +672,132 @@ wcstring reformat_for_screen(const wcstring &msg) 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'); + // 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) - { + line_width = 0; + } else { + // Print the token. + wchar_t *token = wcsndup(start, pos - start); + if ((line_width + (line_width != 0 ? 1 : 0) + tok_width) > screen_width) { buff.push_back(L'\n'); - line_width=0; + line_width = 0; } - buff.append(format_string(L"%ls%ls", line_width?L" ":L"", token)); + buff.append(format_string(L"%ls%ls", line_width ? L" " : L"", token)); free(token); - line_width += (line_width!=0?1:0) + tok_width; + line_width += (line_width != 0 ? 1 : 0) + tok_width; } - /* - Break on end of string - */ - if (!*pos) - { + // Break on end of string. + if (!*pos) { break; } - start=pos; + start = pos; } - } - else - { + } else { buff.append(msg); } buff.push_back(L'\n'); return buff; } -/* Escape a string, storing the result in out_str */ -static void escape_string_internal(const wchar_t *orig_in, size_t in_len, wcstring *out_str, escape_flags_t flags) -{ +/// Escape a string, storing the result in out_str. +static void escape_string_internal(const wchar_t *orig_in, size_t in_len, wcstring *out_str, + escape_flags_t flags) { assert(orig_in != NULL); const wchar_t *in = orig_in; bool escape_all = !!(flags & ESCAPE_ALL); - bool no_quoted = !!(flags & ESCAPE_NO_QUOTED); + bool no_quoted = !!(flags & ESCAPE_NO_QUOTED); bool no_tilde = !!(flags & ESCAPE_NO_TILDE); - int need_escape=0; - int need_complex_escape=0; + int need_escape = 0; + int need_complex_escape = 0; - /* Avoid dereferencing all over the place */ + // Avoid dereferencing all over the place. wcstring &out = *out_str; - if (!no_quoted && in_len == 0) - { + if (!no_quoted && in_len == 0) { out.assign(L"''"); return; } - while (*in != 0) - { - - if ((*in >= ENCODE_DIRECT_BASE) && - (*in < ENCODE_DIRECT_BASE+256)) - { + while (*in != 0) { + if ((*in >= ENCODE_DIRECT_BASE) && (*in < ENCODE_DIRECT_BASE + 256)) { int val = *in - ENCODE_DIRECT_BASE; int tmp; out += L'\\'; out += L'X'; - tmp = val/16; - out += tmp > 9? L'a'+(tmp-10):L'0'+tmp; + tmp = val / 16; + out += tmp > 9 ? L'a' + (tmp - 10) : L'0' + tmp; - tmp = val%16; - out += tmp > 9? L'a'+(tmp-10):L'0'+tmp; - need_escape=need_complex_escape=1; + tmp = val % 16; + out += tmp > 9 ? L'a' + (tmp - 10) : L'0' + tmp; + need_escape = need_complex_escape = 1; - } - else - { + } else { wchar_t c = *in; - switch (c) - { - case L'\t': + switch (c) { + case L'\t': { out += L'\\'; out += L't'; - need_escape=need_complex_escape=1; + need_escape = need_complex_escape = 1; break; - - case L'\n': + } + case L'\n': { out += L'\\'; out += L'n'; - need_escape=need_complex_escape=1; + need_escape = need_complex_escape = 1; break; - - case L'\b': + } + case L'\b': { out += L'\\'; out += L'b'; - need_escape=need_complex_escape=1; + need_escape = need_complex_escape = 1; break; - - case L'\r': + } + case L'\r': { out += L'\\'; out += L'r'; - need_escape=need_complex_escape=1; + need_escape = need_complex_escape = 1; break; - - case L'\x1b': + } + case L'\x1b': { out += L'\\'; out += L'e'; - need_escape=need_complex_escape=1; + need_escape = need_complex_escape = 1; break; - - + } case L'\\': - case L'\'': - { - need_escape=need_complex_escape=1; - if (escape_all) - out += L'\\'; + case L'\'': { + need_escape = need_complex_escape = 1; + if (escape_all) out += L'\\'; out += *in; break; } - - - // Experimental fix for #1614 - // The hope is that any time these appear in a string, they came from wildcard expansion - case ANY_CHAR: + case ANY_CHAR: { + // Experimental fix for #1614. The hope is that any time these appear in a + // string, they came from wildcard expansion. out += L'?'; break; - - case ANY_STRING: + } + case ANY_STRING: { out += L'*'; break; - - case ANY_STRING_RECURSIVE: + } + case ANY_STRING_RECURSIVE: { out += L"**"; break; - + } case L'&': case L'$': case L' ': @@ -1038,43 +817,33 @@ static void escape_string_internal(const wchar_t *orig_in, size_t in_len, wcstri case L';': case L'"': case L'%': - case L'~': - { - if (! no_tilde || c != L'~') - { - need_escape=1; - if (escape_all) - out += L'\\'; + case L'~': { + if (!no_tilde || c != L'~') { + need_escape = 1; + if (escape_all) out += L'\\'; } out += *in; break; } - default: - { - if (*in < 32) - { - if (*in <27 && *in > 0) - { + default: { + if (*in < 32) { + if (*in < 27 && *in > 0) { out += L'\\'; out += L'c'; - out += L'a' + *in -1; + out += L'a' + *in - 1; - need_escape=need_complex_escape=1; + need_escape = need_complex_escape = 1; break; - } - - int tmp = (*in)%16; + int tmp = (*in) % 16; out += L'\\'; out += L'x'; - out += ((*in>15)? L'1' : L'0'); - out += tmp > 9? L'a'+(tmp-10):L'0'+tmp; - need_escape=need_complex_escape=1; - } - else - { + out += ((*in > 15) ? L'1' : L'0'); + out += tmp > 9 ? L'a' + (tmp - 10) : L'0' + tmp; + need_escape = need_complex_escape = 1; + } else { out += *in; } break; @@ -1085,12 +854,8 @@ static void escape_string_internal(const wchar_t *orig_in, size_t in_len, wcstri in++; } - /* - Use quoted escaping if possible, since most people find it - easier to read. - */ - if (!no_quoted && need_escape && !need_complex_escape && escape_all) - { + // Use quoted escaping if possible, since most people find it easier to read. + if (!no_quoted && need_escape && !need_complex_escape && escape_all) { wchar_t single_quote = L'\''; out.clear(); out.reserve(2 + in_len); @@ -1100,59 +865,52 @@ static void escape_string_internal(const wchar_t *orig_in, size_t in_len, wcstri } } -wcstring escape(const wchar_t *in, escape_flags_t flags) -{ +wcstring escape(const wchar_t *in, escape_flags_t flags) { wcstring result; escape_string_internal(in, wcslen(in), &result, flags); return result; } -wcstring escape_string(const wcstring &in, escape_flags_t flags) -{ +wcstring escape_string(const wcstring &in, escape_flags_t flags) { wcstring result; escape_string_internal(in.c_str(), in.size(), &result, flags); return result; } -/* Helper to return the last character in a string, or NOT_A_WCHAR */ -static wint_t string_last_char(const wcstring &str) -{ +/// Helper to return the last character in a string, or NOT_A_WCHAR. +static wint_t string_last_char(const wcstring &str) { size_t len = str.size(); return len == 0 ? NOT_A_WCHAR : str.at(len - 1); } -/* Given a null terminated string starting with a backslash, read the escape as if it is unquoted, appending to result. Return the number of characters consumed, or 0 on error */ -size_t read_unquoted_escape(const wchar_t *input, wcstring *result, bool allow_incomplete, bool unescape_special) -{ - if (input[0] != L'\\') - { - // not an escape - return 0; +/// Given a null terminated string starting with a backslash, read the escape as if it is unquoted, +/// appending to result. Return the number of characters consumed, or 0 on error. +size_t read_unquoted_escape(const wchar_t *input, wcstring *result, bool allow_incomplete, + bool unescape_special) { + if (input[0] != L'\\') { + return 0; // not an escape } - /* Here's the character we'll ultimately append, or NOT_A_WCHAR for none. Note that L'\0' is a valid thing to append. */ + // Here's the character we'll ultimately append, or NOT_A_WCHAR for none. Note that L'\0' is a + // valid thing to append. wint_t result_char_or_none = NOT_A_WCHAR; bool errored = false; - size_t in_pos = 1; //in_pos always tracks the next character to read (and therefore the number of characters read so far) + size_t in_pos = 1; // in_pos always tracks the next character to read (and therefore the number + // of characters read so far) const wchar_t c = input[in_pos++]; - switch (c) - { - - /* A null character after a backslash is an error */ - case L'\0': - { - /* Adjust in_pos to only include the backslash */ + switch (c) { + // A null character after a backslash is an error. + case L'\0': { + // Adjust in_pos to only include the backslash. assert(in_pos > 0); in_pos--; - /* It's an error, unless we're allowing incomplete escapes */ - if (! allow_incomplete) - errored = true; + // It's an error, unless we're allowing incomplete escapes. + if (!allow_incomplete) errored = true; break; } - - /* Numeric escape sequences. No prefix means octal escape, otherwise hexadecimal. */ + // Numeric escape sequences. No prefix means octal escape, otherwise hexadecimal. case L'0': case L'1': case L'2': @@ -1164,200 +922,144 @@ size_t read_unquoted_escape(const wchar_t *input, wcstring *result, bool allow_i case L'u': case L'U': case L'x': - case L'X': - { - long long res=0; - size_t chars=2; - int base=16; - + case L'X': { + long long res = 0; + size_t chars = 2; + int base = 16; bool byte_literal = false; wchar_t max_val = ASCII_MAX; - switch (c) - { - case L'u': - { - chars=4; + switch (c) { + case L'u': { + chars = 4; max_val = UCS2_MAX; break; } - - case L'U': - { - chars=8; + case L'U': { + chars = 8; max_val = WCHAR_MAX; - // Don't exceed the largest Unicode code point - see #1107 - if (0x10FFFF < max_val) - max_val = (wchar_t)0x10FFFF; - + // Don't exceed the largest Unicode code point - see #1107. + if (0x10FFFF < max_val) max_val = (wchar_t)0x10FFFF; break; } - - case L'x': - { + case L'x': { chars = 2; max_val = ASCII_MAX; break; } - - case L'X': - { + case L'X': { byte_literal = true; max_val = BYTE_MAX; break; } - - default: - { - base=8; - chars=3; - // note that in_pos currently is just after the first post-backslash character; we want to start our escape from there + default: { + base = 8; + chars = 3; + // Note that in_pos currently is just after the first post-backslash character; + // we want to start our escape from there. assert(in_pos > 0); in_pos--; break; } } - for (size_t i=0; i= L'a' && sequence_char <= (L'a'+32)) - { - result_char_or_none = sequence_char-L'a'+1; - } - else if (sequence_char >= L'A' && sequence_char <= (L'A'+32)) - { - result_char_or_none = sequence_char-L'A'+1; - } - else - { + if (sequence_char >= L'a' && sequence_char <= (L'a' + 32)) { + result_char_or_none = sequence_char - L'a' + 1; + } else if (sequence_char >= L'A' && sequence_char <= (L'A' + 32)) { + result_char_or_none = sequence_char - L'A' + 1; + } else { errored = true; } break; } - - /* \x1b means escape */ - case L'e': - { + // \x1b means escape. + case L'e': { result_char_or_none = L'\x1b'; break; } - - /* - \f means form feed - */ - case L'f': - { + // \f means form feed. + case L'f': { result_char_or_none = L'\f'; break; } - - /* - \n means newline - */ - case L'n': - { + // \n means newline. + case L'n': { result_char_or_none = L'\n'; break; } - - /* - \r means carriage return - */ - case L'r': - { + // \r means carriage return. + case L'r': { result_char_or_none = L'\r'; break; } - - /* - \t means tab - */ - case L't': - { + // \t means tab. + case L't': { result_char_or_none = L'\t'; break; } - - /* - \v means vertical tab - */ - case L'v': - { + // \v means vertical tab. + case L'v': { result_char_or_none = L'\v'; break; } - - /* If a backslash is followed by an actual newline, swallow them both */ - case L'\n': - { + // If a backslash is followed by an actual newline, swallow them both. + case L'\n': { result_char_or_none = NOT_A_WCHAR; break; } - - default: - { - if (unescape_special) - result->push_back(INTERNAL_SEPARATOR); + default: { + if (unescape_special) result->push_back(INTERNAL_SEPARATOR); result_char_or_none = c; break; } } - if (! errored && result_char_or_none != NOT_A_WCHAR) - { + if (!errored && result_char_or_none != NOT_A_WCHAR) { wchar_t result_char = static_cast(result_char_or_none); - // if result_char is not NOT_A_WCHAR, it must be a valid wchar + // If result_char is not NOT_A_WCHAR, it must be a valid wchar. assert((wint_t)result_char == result_char_or_none); result->push_back(result_char); } return errored ? 0 : in_pos; } -/* Returns the unescaped version of input_str into output_str (by reference). Returns true if successful. If false, the contents of output_str are undefined (!) */ -static bool unescape_string_internal(const wchar_t * const input, const size_t input_len, wcstring *output_str, unescape_flags_t flags) -{ - /* Set up result string, which we'll swap with the output on success */ +/// Returns the unescaped version of input_str into output_str (by reference). Returns true if +/// successful. If false, the contents of output_str are undefined (!). +static bool unescape_string_internal(const wchar_t *const input, const size_t input_len, + wcstring *output_str, unescape_flags_t flags) { + // Set up result string, which we'll swap with the output on success. wcstring result; result.reserve(input_len); @@ -1367,310 +1069,228 @@ static bool unescape_string_internal(const wchar_t * const input, const size_t i int bracket_count = 0; bool errored = false; - enum - { - mode_unquoted, - mode_single_quotes, - mode_double_quotes - } mode = mode_unquoted; + enum { mode_unquoted, mode_single_quotes, mode_double_quotes } mode = mode_unquoted; - for (size_t input_position = 0; input_position < input_len && ! errored; input_position++) - { + for (size_t input_position = 0; input_position < input_len && !errored; input_position++) { const wchar_t c = input[input_position]; - /* Here's the character we'll append to result, or NOT_A_WCHAR to suppress it */ + // Here's the character we'll append to result, or NOT_A_WCHAR to suppress it. wint_t to_append_or_none = c; - if (mode == mode_unquoted) - { - - switch (c) - { - case L'\\': - { - /* Backslashes (escapes) are complicated and may result in errors, or appending INTERNAL_SEPARATORs, so we have to handle them specially */ - size_t escape_chars = read_unquoted_escape(input + input_position, &result, allow_incomplete, unescape_special); - if (escape_chars == 0) - { - /* A 0 return indicates an error */ + if (mode == mode_unquoted) { + switch (c) { + case L'\\': { + // Backslashes (escapes) are complicated and may result in errors, or appending + // INTERNAL_SEPARATORs, so we have to handle them specially. + size_t escape_chars = read_unquoted_escape(input + input_position, &result, + allow_incomplete, unescape_special); + if (escape_chars == 0) { + // A 0 return indicates an error. errored = true; - } - else - { - /* Skip over the characters we read, minus one because the outer loop will increment it */ + } else { + // Skip over the characters we read, minus one because the outer loop will + // increment it. assert(escape_chars > 0); input_position += escape_chars - 1; } - /* We've already appended, don't append anything else */ + // We've already appended, don't append anything else. to_append_or_none = NOT_A_WCHAR; break; } - - case L'~': - { - if (unescape_special && (input_position == 0)) - { + case L'~': { + if (unescape_special && (input_position == 0)) { to_append_or_none = HOME_DIRECTORY; } break; } - - case L'%': - { - if (unescape_special && (input_position == 0)) - { + case L'%': { + if (unescape_special && (input_position == 0)) { to_append_or_none = PROCESS_EXPAND; } break; } - - case L'*': - { - if (unescape_special) - { - /* In general, this is ANY_STRING. But as a hack, if the last appended char is ANY_STRING, delete the last char and store ANY_STRING_RECURSIVE to reflect the fact that ** is the recursive wildcard. */ - if (string_last_char(result) == ANY_STRING) - { + case L'*': { + if (unescape_special) { + // In general, this is ANY_STRING. But as a hack, if the last appended char + // is ANY_STRING, delete the last char and store ANY_STRING_RECURSIVE to + // reflect the fact that ** is the recursive wildcard. + if (string_last_char(result) == ANY_STRING) { assert(result.size() > 0); result.resize(result.size() - 1); to_append_or_none = ANY_STRING_RECURSIVE; - } - else - { + } else { to_append_or_none = ANY_STRING; } } break; } - - case L'?': - { - if (unescape_special) - { + case L'?': { + if (unescape_special) { to_append_or_none = ANY_CHAR; } break; } - - case L'$': - { - if (unescape_special) - { + case L'$': { + if (unescape_special) { to_append_or_none = VARIABLE_EXPAND; } break; } - - case L'{': - { - if (unescape_special) - { + case L'{': { + if (unescape_special) { bracket_count++; to_append_or_none = BRACKET_BEGIN; } break; } - - case L'}': - { - if (unescape_special) - { + case L'}': { + if (unescape_special) { bracket_count--; to_append_or_none = BRACKET_END; } break; } - - case L',': - { - /* If the last character was a separator, then treat this as a literal comma */ - if (unescape_special && bracket_count > 0 && string_last_char(result) != BRACKET_SEP) - { + case L',': { + // If the last character was a separator, then treat this as a literal comma. + if (unescape_special && bracket_count > 0 && + string_last_char(result) != BRACKET_SEP) { to_append_or_none = BRACKET_SEP; } break; } - - case L'\'': - { + case L'\'': { mode = mode_single_quotes; to_append_or_none = unescape_special ? INTERNAL_SEPARATOR : NOT_A_WCHAR; break; } - - case L'\"': - { + case L'\"': { mode = mode_double_quotes; to_append_or_none = unescape_special ? INTERNAL_SEPARATOR : NOT_A_WCHAR; break; } } - } - else if (mode == mode_single_quotes) - { - if (c == L'\\') - { - /* A backslash may or may not escape something in single quotes */ - switch (input[input_position + 1]) - { + } else if (mode == mode_single_quotes) { + if (c == L'\\') { + // A backslash may or may not escape something in single quotes. + switch (input[input_position + 1]) { case '\\': - case L'\'': - { + case L'\'': { to_append_or_none = input[input_position + 1]; - input_position += 1; /* Skip over the backslash */ + input_position += 1; // skip over the backslash break; } - - case L'\0': - { - if (!allow_incomplete) - { + case L'\0': { + if (!allow_incomplete) { errored = true; - } - else - { - // PCA this line had the following cryptic comment: - // 'We may ever escape a NULL character, but still appending a \ in case I am wrong.' - // Not sure what it means or the importance of this + } else { + // PCA this line had the following cryptic comment: 'We may ever escape + // a NULL character, but still appending a \ in case I am wrong.' Not + // sure what it means or the importance of this. input_position += 1; /* Skip over the backslash */ to_append_or_none = L'\\'; } + break; } - break; - - default: - { - /* Literal backslash that doesn't escape anything! Leave things alone; we'll append the backslash itself */ + default: { + // Literal backslash that doesn't escape anything! Leave things alone; we'll + // append the backslash itself. break; } } - } - else if (c == L'\'') - { + } else if (c == L'\'') { to_append_or_none = unescape_special ? INTERNAL_SEPARATOR : NOT_A_WCHAR; mode = mode_unquoted; } - } - else if (mode == mode_double_quotes) - { - switch (c) - { - case L'"': - { + } else if (mode == mode_double_quotes) { + switch (c) { + case L'"': { mode = mode_unquoted; to_append_or_none = unescape_special ? INTERNAL_SEPARATOR : NOT_A_WCHAR; break; } - - case '\\': - { - switch (input[input_position + 1]) - { - case L'\0': - { - if (!allow_incomplete) - { + case '\\': { + switch (input[input_position + 1]) { + case L'\0': { + if (!allow_incomplete) { errored = true; - } - else - { + } else { to_append_or_none = L'\0'; } + break; } - break; - case '\\': case L'$': - case '"': - { + case '"': { to_append_or_none = input[input_position + 1]; input_position += 1; /* Skip over the backslash */ break; } - - case '\n': - { + case '\n': { /* Swallow newline */ to_append_or_none = NOT_A_WCHAR; input_position += 1; /* Skip over the backslash */ break; } - - default: - { - /* Literal backslash that doesn't escape anything! Leave things alone; we'll append the backslash itself */ + default: { + /* Literal backslash that doesn't escape anything! Leave things alone; + * we'll append the backslash itself */ break; } } break; } - - case '$': - { - if (unescape_special) - { + case '$': { + if (unescape_special) { to_append_or_none = VARIABLE_EXPAND_SINGLE; } break; } - } } - /* Now maybe append the char */ - if (to_append_or_none != NOT_A_WCHAR) - { + // Now maybe append the char. + if (to_append_or_none != NOT_A_WCHAR) { wchar_t to_append_char = static_cast(to_append_or_none); - // if result_char is not NOT_A_WCHAR, it must be a valid wchar + // If result_char is not NOT_A_WCHAR, it must be a valid wchar. assert((wint_t)to_append_char == to_append_or_none); result.push_back(to_append_char); } } - /* Return the string by reference, and then success */ - if (! errored) - { + // Return the string by reference, and then success. + if (!errored) { output_str->swap(result); } - return ! errored; + return !errored; } -bool unescape_string_in_place(wcstring *str, unescape_flags_t escape_special) -{ +bool unescape_string_in_place(wcstring *str, unescape_flags_t escape_special) { assert(str != NULL); wcstring output; bool success = unescape_string_internal(str->c_str(), str->size(), &output, escape_special); - if (success) - { + if (success) { str->swap(output); } return success; } -bool unescape_string(const wchar_t *input, wcstring *output, unescape_flags_t escape_special) -{ +bool unescape_string(const wchar_t *input, wcstring *output, unescape_flags_t escape_special) { bool success = unescape_string_internal(input, wcslen(input), output, escape_special); - if (! success) - output->clear(); + if (!success) output->clear(); return success; } -bool unescape_string(const wcstring &input, wcstring *output, unescape_flags_t escape_special) -{ +bool unescape_string(const wcstring &input, wcstring *output, unescape_flags_t escape_special) { bool success = unescape_string_internal(input.c_str(), input.size(), output, escape_special); - if (! success) - output->clear(); + if (!success) output->clear(); return success; } - -void common_handle_winch(int signal) -{ - /* don't run ioctl() here, it's not safe to use in signals */ +void common_handle_winch(int signal) { + // Don't run ioctl() here, it's not safe to use in signals. termsize_valid = false; } -/* updates termsize as needed, and returns a copy of the winsize. */ -static struct winsize get_current_winsize() -{ +/// Updates termsize as needed, and returns a copy of the winsize. +static struct winsize get_current_winsize() { #ifndef HAVE_WINSIZE struct winsize retval = {0}; retval.ws_col = 80; @@ -1679,11 +1299,9 @@ static struct winsize get_current_winsize() #endif scoped_rwlock guard(termsize_rwlock, true); struct winsize retval = termsize; - if (!termsize_valid) - { + if (!termsize_valid) { struct winsize size; - if (ioctl(1,TIOCGWINSZ,&size) == 0) - { + if (ioctl(1, TIOCGWINSZ, &size) == 0) { retval = size; guard.upgrade(); termsize = retval; @@ -1693,277 +1311,210 @@ static struct winsize get_current_winsize() return retval; } -int common_get_width() -{ - return get_current_winsize().ws_col; -} +int common_get_width() { return get_current_winsize().ws_col; } +int common_get_height() { return get_current_winsize().ws_row; } -int common_get_height() -{ - return get_current_winsize().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) - { + if (next_pos == wcstring::npos) { next_pos = end; } out.resize(out.size() + 1); out.back().assign(val, pos, next_pos - pos); - pos = next_pos + 1; //skip the separator, or skip past the end + pos = next_pos + 1; // skip the separator, or skip past the end } } -bool string_prefixes_string(const wchar_t *proposed_prefix, const wcstring &value) -{ +bool string_prefixes_string(const wchar_t *proposed_prefix, const wcstring &value) { size_t prefix_size = wcslen(proposed_prefix); return prefix_size <= value.size() && value.compare(0, prefix_size, proposed_prefix) == 0; } -bool string_prefixes_string(const wcstring &proposed_prefix, const wcstring &value) -{ +bool string_prefixes_string(const wcstring &proposed_prefix, const wcstring &value) { size_t prefix_size = proposed_prefix.size(); 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; + 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; + 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; + return suffix_size <= value.size() && + value.compare(value.size() - suffix_size, suffix_size, proposed_suffix) == 0; } -// Returns true if seq, represented as a subsequence, is contained within string -static bool subsequence_in_string(const wcstring &seq, const wcstring &str) -{ - /* Impossible if seq is larger than string */ - if (seq.size() > str.size()) - { +/// Returns true if seq, represented as a subsequence, is contained within string. +static bool subsequence_in_string(const wcstring &seq, const wcstring &str) { + // Impossible if seq is larger than string. + if (seq.size() > str.size()) { return false; } - /* Empty strings are considered to be subsequences of everything */ - if (seq.empty()) - { + // Empty strings are considered to be subsequences of everything. + if (seq.empty()) { return true; } size_t str_idx, seq_idx; - for (seq_idx = str_idx = 0; seq_idx < seq.size() && str_idx < str.size(); seq_idx++) - { + for (seq_idx = str_idx = 0; seq_idx < seq.size() && str_idx < str.size(); seq_idx++) { wchar_t c = seq.at(seq_idx); size_t char_loc = str.find(c, str_idx); - if (char_loc == wcstring::npos) - { - /* Didn't find this character */ - break; - } - else - { - /* We found it. Continue the search just after it. */ - str_idx = char_loc + 1; + if (char_loc == wcstring::npos) { + break; // didn't find this character + } else { + str_idx = char_loc + 1; // we found it, continue the search just after it } } - /* We succeeded if we exhausted our sequence */ + // We succeeded if we exhausted our sequence. assert(seq_idx <= seq.size()); return seq_idx == seq.size(); } -string_fuzzy_match_t::string_fuzzy_match_t(enum fuzzy_match_type_t t, size_t distance_first, size_t distance_second) : - type(t), - match_distance_first(distance_first), - match_distance_second(distance_second) -{ -} +string_fuzzy_match_t::string_fuzzy_match_t(enum fuzzy_match_type_t t, size_t distance_first, + size_t distance_second) + : type(t), match_distance_first(distance_first), match_distance_second(distance_second) {} - -string_fuzzy_match_t string_fuzzy_match_string(const wcstring &string, const wcstring &match_against, fuzzy_match_type_t limit_type) -{ - // Distances are generally the amount of text not matched +string_fuzzy_match_t string_fuzzy_match_string(const wcstring &string, + const wcstring &match_against, + fuzzy_match_type_t limit_type) { + // Distances are generally the amount of text not matched. string_fuzzy_match_t result(fuzzy_match_none, 0, 0); size_t location; - if (limit_type >= fuzzy_match_exact && string == match_against) - { + if (limit_type >= fuzzy_match_exact && string == match_against) { result.type = fuzzy_match_exact; - } - else if (limit_type >= fuzzy_match_prefix && string_prefixes_string(string, match_against)) - { + } else if (limit_type >= fuzzy_match_prefix && string_prefixes_string(string, match_against)) { result.type = fuzzy_match_prefix; assert(match_against.size() >= string.size()); result.match_distance_first = match_against.size() - string.size(); - } - else if (limit_type >= fuzzy_match_case_insensitive && wcscasecmp(string.c_str(), match_against.c_str()) == 0) - { + } else if (limit_type >= fuzzy_match_case_insensitive && + wcscasecmp(string.c_str(), match_against.c_str()) == 0) { result.type = fuzzy_match_case_insensitive; - } - else if (limit_type >= fuzzy_match_prefix_case_insensitive && string_prefixes_string_case_insensitive(string, match_against)) - { + } else if (limit_type >= fuzzy_match_prefix_case_insensitive && + string_prefixes_string_case_insensitive(string, match_against)) { result.type = fuzzy_match_prefix_case_insensitive; assert(match_against.size() >= string.size()); result.match_distance_first = match_against.size() - string.size(); - } - else if (limit_type >= fuzzy_match_substring && (location = match_against.find(string)) != wcstring::npos) - { - // string is contained within match against + } else if (limit_type >= fuzzy_match_substring && + (location = match_against.find(string)) != wcstring::npos) { + // String is contained within match against. result.type = fuzzy_match_substring; assert(match_against.size() >= string.size()); result.match_distance_first = match_against.size() - string.size(); - result.match_distance_second = location; //prefer earlier matches - } - else if (limit_type >= fuzzy_match_subsequence_insertions_only && subsequence_in_string(string, match_against)) - { + result.match_distance_second = location; // prefer earlier matches + } else if (limit_type >= fuzzy_match_subsequence_insertions_only && + subsequence_in_string(string, match_against)) { result.type = fuzzy_match_subsequence_insertions_only; assert(match_against.size() >= string.size()); result.match_distance_first = match_against.size() - string.size(); - // it would be nice to prefer matches with greater matching runs here + // It would be nice to prefer matches with greater matching runs here. } return result; } -template -static inline int compare_ints(T a, T b) -{ +template +static inline int compare_ints(T a, T b) { if (a < b) return -1; if (a == b) return 0; return 1; } -// Compare types; if the types match, compare distances -int string_fuzzy_match_t::compare(const string_fuzzy_match_t &rhs) const -{ - if (this->type != rhs.type) - { +/// Compare types; if the types match, compare distances. +int string_fuzzy_match_t::compare(const string_fuzzy_match_t &rhs) const { + if (this->type != rhs.type) { return compare_ints(this->type, rhs.type); - } - else if (this->match_distance_first != rhs.match_distance_first) - { + } else if (this->match_distance_first != rhs.match_distance_first) { return compare_ints(this->match_distance_first, rhs.match_distance_first); - } - else if (this->match_distance_second != rhs.match_distance_second) - { + } else if (this->match_distance_second != rhs.match_distance_second) { return compare_ints(this->match_distance_second, rhs.match_distance_second); } - return 0; //equal + return 0; // equal } -bool list_contains_string(const wcstring_list_t &list, const wcstring &str) -{ +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; + while ((stat_res = wstat(d, &buf)) != 0) { + if (errno != EAGAIN) break; } - if (stat_res == 0) - { - if (S_ISDIR(buf.st_mode)) - { + if (stat_res == 0) { + if (S_ISDIR(buf.st_mode)) { ok = 1; } - } - else - { - if (errno == ENOENT) - { + } else { + if (errno == ENOENT) { wcstring dir = wdirname(d); - if (!create_directory(dir)) - { - if (!wmkdir(d, 0700)) - { + if (!create_directory(dir)) { + if (!wmkdir(d, 0700)) { ok = 1; } } } } - return ok?0:-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."), +__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); } -wcstring format_size(long long sz) -{ +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) - { + if (sz < 0) { result.append(L"unknown"); - } - else if (sz < 1) - { + } else if (sz < 1) { result.append(_(L"empty")); - } - else if (sz < 1024) - { + } else if (sz < 1024) { result.append(format_string(L"%lldB", sz)); - } - else - { + } else { int i; - for (i=0; sz_name[i]; i++) - { - if (sz < (1024*1024) || !sz_name[i+1]) - { - long isz = ((long)sz)/1024; + 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])); + 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) -{ +/// Crappy function to extract the most significant digit of an unsigned long long value. +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; } @@ -1971,67 +1522,45 @@ 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); + 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++; + 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 + const size_t max_len = buff_size - 1; // need to leave room for a null terminator memset(buff, 0, buff_size); size_t idx = 0; - const char * const sz_name[]= - { - "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", NULL - }; - if (sz < 1) - { + const char *const sz_name[] = {"kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", NULL}; + if (sz < 1) { strncpy(buff, "empty", buff_size); - } - else if (sz < 1024) - { + } else if (sz < 1024) { append_ull(buff, sz, &idx, max_len); append_str(buff, "B", &idx, max_len); - } - 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) - { + } 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) { append_ull(buff, isz, &idx, max_len); - } - else - { - if (isz == 0) - { + } else { + if (isz == 0) { append_str(buff, "0", &idx, max_len); - } - else - { + } else { append_ull(buff, isz, &idx, max_len); } - // Maybe append a single fraction digit + // Maybe append a single fraction digit. unsigned long long remainder = sz % 1024; - if (remainder > 0) - { + if (remainder > 0) { char tmp[3] = {'.', extract_most_significant_digit(&remainder), 0}; append_str(buff, tmp, &idx, max_len); } @@ -2044,70 +1573,52 @@ void format_size_safe(char buff[128], unsigned long long sz) } } -double timef() -{ - int time_res; +double timef() { struct timeval tv; + int time_res = gettimeofday(&tv, 0); - 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. - */ + 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; + return (double)tv.tv_sec + 0.000001 * tv.tv_usec; } -void exit_without_destructors(int code) -{ - _exit(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 */ -void convert_wide_array_to_narrow(const null_terminated_array_t &wide_arr, null_terminated_array_t *output) -{ +/// Helper function to convert from a null_terminated_array_t to a +/// null_terminated_array_t. +void convert_wide_array_to_narrow(const null_terminated_array_t &wide_arr, + null_terminated_array_t *output) { const wchar_t *const *arr = wide_arr.get(); - if (! arr) - { + if (!arr) { output->clear(); return; } 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])); } output->set(list); } -void append_path_component(wcstring &path, const wcstring &component) -{ - if (path.empty() || component.empty()) - { +void append_path_component(wcstring &path, const wcstring &component) { + 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 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) - { - // Too many slashes + } else if (path_slash && comp_slash) { + // Too many slashes. path.erase(path_len - 1, 1); } path.append(component); @@ -2115,304 +1626,253 @@ 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; } -/* Notice when we've forked */ +/// Notice when we've forked. static pid_t initial_pid = 0; -/* Be able to restore the term's foreground process group */ +/// Be able to restore the term's foreground process group. static pid_t initial_foreground_process_group = -1; -bool is_forked_child(void) -{ - /* Just bail if nobody's called setup_fork_guards, e.g. some of our tools */ - if (! initial_pid) return false; +bool is_forked_child(void) { + // Just bail if nobody's called setup_fork_guards, e.g. some of our tools. + 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); } return is_child_of_fork; } -void setup_fork_guards(void) -{ - /* Notice when we fork by stashing our pid. This seems simpler than pthread_atfork(). */ +void setup_fork_guards(void) { + // Notice when we fork by stashing our pid. This seems simpler than pthread_atfork(). initial_pid = getpid(); } -void save_term_foreground_process_group(void) -{ +void save_term_foreground_process_group(void) { initial_foreground_process_group = tcgetpgrp(STDIN_FILENO); } -void restore_term_foreground_process_group(void) -{ - if (initial_foreground_process_group != -1) - { - /* This is called during shutdown and from a signal handler. We don't bother to complain on failure. */ - if (0 > tcsetpgrp(STDIN_FILENO, initial_foreground_process_group)) - { - /* Ignore failure */ - } +void restore_term_foreground_process_group(void) { + if (initial_foreground_process_group != -1) { + // This is called during shutdown and from a signal handler. We don't bother to complain on + // failure. + tcsetpgrp(STDIN_FILENO, initial_foreground_process_group); } } -bool is_main_thread() -{ +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) - { - fprintf(stderr, "Warning: %s called off of main thread. Break on debug_thread_error to debug.\n", who); +void assert_is_main_thread(const char *who) { + 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(); } } -void assert_is_not_forked_child(const char *who) -{ - if (is_forked_child()) - { - fprintf(stderr, "Warning: %s called in a forked child. Break on debug_thread_error to debug.\n", who); +void assert_is_not_forked_child(const char *who) { + 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(); } } -void assert_is_background_thread(const char *who) -{ - 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); +void assert_is_background_thread(const char *who) { + 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(); } } -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)) - { - fprintf(stderr, "Warning: %s is not locked when it should be in '%s'. Break on debug_thread_error to debug.\n", who, caller); +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)) { + 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) -{ - assert(! locked); - assert(! is_forked_child()); +void scoped_lock::lock(void) { + assert(!locked); + assert(!is_forked_child()); VOMIT_ON_FAILURE_NO_ERRNO(pthread_mutex_lock(lock_obj)); locked = true; } -void scoped_lock::unlock(void) -{ +void scoped_lock::unlock(void) { assert(locked); - assert(! is_forked_child()); + assert(!is_forked_child()); VOMIT_ON_FAILURE_NO_ERRNO(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(mutex_lock_t &lock) : lock_obj(&lock.mutex), locked(false) { this->lock(); } -scoped_lock::scoped_lock(mutex_lock_t &lock) : lock_obj(&lock.mutex), locked(false) -{ - this->lock(); -} - -scoped_lock::~scoped_lock() -{ +scoped_lock::~scoped_lock() { if (locked) this->unlock(); } -void scoped_rwlock::lock(void) -{ - assert(! (locked || locked_shared)); - assert(! is_forked_child()); +void scoped_rwlock::lock(void) { + assert(!(locked || locked_shared)); + assert(!is_forked_child()); VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_rdlock(rwlock_obj)); locked = true; } -void scoped_rwlock::unlock(void) -{ +void scoped_rwlock::unlock(void) { assert(locked); - assert(! is_forked_child()); + assert(!is_forked_child()); VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_unlock(rwlock_obj)); locked = false; } -void scoped_rwlock::lock_shared(void) -{ - assert(! (locked || locked_shared)); - assert(! is_forked_child()); +void scoped_rwlock::lock_shared(void) { + assert(!(locked || locked_shared)); + assert(!is_forked_child()); VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_wrlock(rwlock_obj)); locked_shared = true; } -void scoped_rwlock::unlock_shared(void) -{ +void scoped_rwlock::unlock_shared(void) { assert(locked_shared); - assert(! is_forked_child()); + assert(!is_forked_child()); VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_unlock(rwlock_obj)); locked_shared = false; } -void scoped_rwlock::upgrade(void) -{ +void scoped_rwlock::upgrade(void) { assert(locked_shared); - assert(! is_forked_child()); + assert(!is_forked_child()); VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_unlock(rwlock_obj)); locked = false; VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_wrlock(rwlock_obj)); locked_shared = true; } -scoped_rwlock::scoped_rwlock(pthread_rwlock_t &rwlock, bool shared) : rwlock_obj(&rwlock), locked(false), locked_shared(false) -{ - if (shared) - { +scoped_rwlock::scoped_rwlock(pthread_rwlock_t &rwlock, bool shared) + : rwlock_obj(&rwlock), locked(false), locked_shared(false) { + if (shared) { this->lock_shared(); - } - else - { + } else { this->lock(); } } -scoped_rwlock::scoped_rwlock(rwlock_t &rwlock, bool shared) : rwlock_obj(&rwlock.rwlock), locked(false), locked_shared(false) -{ - if (shared) - { +scoped_rwlock::scoped_rwlock(rwlock_t &rwlock, bool shared) + : rwlock_obj(&rwlock.rwlock), locked(false), locked_shared(false) { + if (shared) { this->lock_shared(); - } - else - { + } else { this->lock(); } } -scoped_rwlock::~scoped_rwlock() -{ - if (locked) - { +scoped_rwlock::~scoped_rwlock() { + if (locked) { this->unlock(); - } - else if (locked_shared) - { + } else if (locked_shared) { this->unlock_shared(); } } -wcstokenizer::wcstokenizer(const wcstring &s, const wcstring &separator) : - buffer(), - str(), - state(), - sep(separator) -{ +wcstokenizer::wcstokenizer(const wcstring &s, const wcstring &separator) + : buffer(), str(), state(), sep(separator) { buffer = wcsdup(s.c_str()); str = buffer; 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() -{ - free(buffer); -} - +wcstokenizer::~wcstokenizer() { free(buffer); } template -static CharType_t **make_null_terminated_array_helper(const std::vector > &argv) -{ +static CharType_t **make_null_terminated_array_helper( + const std::vector > &argv) { size_t count = argv.size(); - /* We allocate everything in one giant block. First compute how much space we need. */ - - /* N + 1 pointers */ + // We allocate everything in one giant block. First compute how much space we need. + // N + 1 pointers. size_t pointers_allocation_len = (count + 1) * sizeof(CharType_t *); - /* In the very unlikely event that CharType_t has stricter alignment requirements than does a pointer, round us up to the size of a CharType_t */ + // In the very unlikely event that CharType_t has stricter alignment requirements than does a + // pointer, round us up to the size of a CharType_t. pointers_allocation_len += sizeof(CharType_t) - 1; pointers_allocation_len -= pointers_allocation_len % sizeof(CharType_t); - /* N null terminated strings */ + // N null terminated strings. size_t strings_allocation_len = 0; - for (size_t i=0; i < count; i++) - { - /* The size of the string, plus a null terminator */ + for (size_t i = 0; i < count; i++) { + // The size of the string, plus a null terminator. strings_allocation_len += (argv.at(i).size() + 1) * sizeof(CharType_t); } - /* Now allocate their sum */ - unsigned char *base = static_cast(malloc(pointers_allocation_len + strings_allocation_len)); - if (! base) return NULL; + // Now allocate their sum. + unsigned char *base = + static_cast(malloc(pointers_allocation_len + strings_allocation_len)); + if (!base) return NULL; - /* Divvy it up into the pointers and strings */ + // Divvy it up into the pointers and strings. CharType_t **pointers = reinterpret_cast(base); CharType_t *strings = reinterpret_cast(base + pointers_allocation_len); - /* Start copying */ - for (size_t i=0; i < count; i++) - { + // Start copying. + for (size_t i = 0; i < count; i++) { const std::basic_string &str = argv.at(i); - // store the current string pointer into self - *pointers++ = strings; - - // copy the string into strings - strings = std::copy(str.begin(), str.end(), strings); - // each string needs a null terminator - *strings++ = (CharType_t)(0); + *pointers++ = strings; // store the current string pointer into self + strings = std::copy(str.begin(), str.end(), strings); // copy the string into strings + *strings++ = (CharType_t)(0); // each string needs a null terminator } - // array of pointers needs a null terminator - *pointers++ = NULL; + *pointers++ = NULL; // array of pointers needs a null terminator - // Make sure we know what we're doing + // Make sure we know what we're doing. assert((unsigned char *)pointers - base == (std::ptrdiff_t)pointers_allocation_len); - assert((unsigned char *)strings - (unsigned char *)pointers == (std::ptrdiff_t)strings_allocation_len); - assert((unsigned char *)strings - base == (std::ptrdiff_t)(pointers_allocation_len + strings_allocation_len)); + assert((unsigned char *)strings - (unsigned char *)pointers == + (std::ptrdiff_t)strings_allocation_len); + assert((unsigned char *)strings - base == + (std::ptrdiff_t)(pointers_allocation_len + strings_allocation_len)); - // Return what we did - return reinterpret_cast(base); + return reinterpret_cast(base); } -wchar_t **make_null_terminated_array(const wcstring_list_t &lst) -{ +wchar_t **make_null_terminated_array(const wcstring_list_t &lst) { return make_null_terminated_array_helper(lst); } -char **make_null_terminated_array(const std::vector &lst) -{ +char **make_null_terminated_array(const std::vector &lst) { return make_null_terminated_array_helper(lst); } diff --git a/src/common.h b/src/common.h index dc6e02366..3d1a771f5 100644 --- a/src/common.h +++ b/src/common.h @@ -1,34 +1,31 @@ -/** \file common.h - Prototypes for various functions, mostly string utilities, that are used by most parts of fish. -*/ +// Prototypes for various functions, mostly string utilities, that are used by most parts of fish. #ifndef FISH_COMMON_H #define FISH_COMMON_H #include "config.h" -#include -#include -#include -#include -#include -#include +#include #include +#include // IWYU pragma: keep +#include +#include +#include #include #include -#include #include -#include -#include +#include +#include // IWYU pragma: keep +#include +#include +#include #include "fallback.h" // IWYU pragma: keep -#include "signal.h" // IWYU pragma: keep +#include "signal.h" // IWYU pragma: keep -/** - Avoid writing the type name twice in a common "static_cast-initialization". - Caveat: This doesn't work with type names containing commas! -*/ -#define CAST_INIT(type, dst, src) type dst = static_cast(src) +/// Avoid writing the type name twice in a common "static_cast-initialization". Caveat: This doesn't +/// work with type names containing commas! +#define CAST_INIT(type, dst, src) type dst = static_cast(src) -/* Common string type */ +// Common string type. typedef std::wstring wcstring; typedef std::vector wcstring_list_t; @@ -54,13 +51,13 @@ typedef std::vector wcstring_list_t; // gives us 32 "characters" for internal use that we can guarantee should not // appear in our input stream. See http://www.unicode.org/faq/private_use.html. #define RESERVED_CHAR_BASE 0xFDD0u -#define RESERVED_CHAR_END 0xFDF0u +#define RESERVED_CHAR_END 0xFDF0u // Split the available noncharacter values into two ranges to ensure there are // no conflicts among the places we use these special characters. #define EXPAND_RESERVED_BASE RESERVED_CHAR_BASE -#define EXPAND_RESERVED_END (EXPAND_RESERVED_BASE + 16) +#define EXPAND_RESERVED_END (EXPAND_RESERVED_BASE + 16) #define WILDCARD_RESERVED_BASE EXPAND_RESERVED_END -#define WILDCARD_RESERVED_END (WILDCARD_RESERVED_BASE + 16) +#define WILDCARD_RESERVED_END (WILDCARD_RESERVED_BASE + 16) // Make sure the ranges defined above don't exceed the range for noncharacters. // This is to make sure we didn't do something stupid in subdividing the // Unicode range for our needs. @@ -81,42 +78,35 @@ typedef std::vector wcstring_list_t; // of at least one use of a codepoint in that range: the Apple symbol (0xF8FF) // on Mac OS X. See http://www.unicode.org/faq/private_use.html. #define ENCODE_DIRECT_BASE 0xF600u -#define ENCODE_DIRECT_END (ENCODE_DIRECT_BASE + 256) -#define INPUT_COMMON_BASE 0xF700u -#define INPUT_COMMON_END (INPUT_COMMON_BASE + 64) +#define ENCODE_DIRECT_END (ENCODE_DIRECT_BASE + 256) +#define INPUT_COMMON_BASE 0xF700u +#define INPUT_COMMON_END (INPUT_COMMON_BASE + 64) -/* Flags for unescape_string functions */ -enum -{ - /* Default behavior */ - UNESCAPE_DEFAULT = 0, - - /* Escape special fish syntax characters like the semicolon */ - UNESCAPE_SPECIAL = 1 << 0, - - /* Allow incomplete escape sequences */ - UNESCAPE_INCOMPLETE = 1 << 1 +// Flags for unescape_string functions. +enum { + UNESCAPE_DEFAULT = 0, // default behavior + UNESCAPE_SPECIAL = 1 << 0, // escape special fish syntax characters like the semicolon + UNESCAPE_INCOMPLETE = 1 << 1 // allow incomplete escape sequences }; typedef unsigned int unescape_flags_t; -/* Flags for the escape() and escape_string() functions */ -enum -{ - /** Escape all characters, including magic characters like the semicolon */ +// Flags for the escape() and escape_string() functions. +enum { + /// Escape all characters, including magic characters like the semicolon. ESCAPE_ALL = 1 << 0, - /** Do not try to use 'simplified' quoted escapes, and do not use empty quotes as the empty string */ + /// 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 */ + /// Do not escape tildes. ESCAPE_NO_TILDE = 1 << 2 }; typedef unsigned int escape_flags_t; -/* Directions */ -enum selection_direction_t -{ - /* visual directions */ +// Directions. +enum selection_direction_t { + // Visual directions. direction_north, direction_east, direction_south, @@ -124,275 +114,237 @@ enum selection_direction_t direction_page_north, direction_page_south, - /* logical directions */ + // Logical directions. direction_next, direction_prev, - /* special value that means deselect */ + // Special value that means deselect. direction_deselect }; -inline bool selection_direction_is_cardinal(selection_direction_t dir) -{ - switch (dir) - { +inline bool selection_direction_is_cardinal(selection_direction_t dir) { + switch (dir) { case direction_north: case direction_page_north: case direction_east: case direction_page_south: case direction_south: - case direction_west: + case direction_west: { return true; - default: - return false; + } + default: { return false; } } } -/** - Helper macro for errors - */ -#define VOMIT_ON_FAILURE(a) do { if (0 != (a)) { VOMIT_ABORT(errno, #a); } } while (0) -#define VOMIT_ON_FAILURE_NO_ERRNO(a) do { int err = (a); if (0 != err) { VOMIT_ABORT(err, #a); } } while (0) -#define VOMIT_ABORT(err, str) do { int code = (err); fprintf(stderr, "%s failed on line %d in file %s: %d (%s)\n", str, __LINE__, __FILE__, code, strerror(code)); abort(); } while(0) +/// Helper macro for errors. +#define VOMIT_ON_FAILURE(a) \ + do { \ + if (0 != (a)) { \ + VOMIT_ABORT(errno, #a); \ + } \ + } while (0) +#define VOMIT_ON_FAILURE_NO_ERRNO(a) \ + do { \ + int err = (a); \ + if (0 != err) { \ + VOMIT_ABORT(err, #a); \ + } \ + } while (0) +#define VOMIT_ABORT(err, str) \ + do { \ + int code = (err); \ + fprintf(stderr, "%s failed on line %d in file %s: %d (%s)\n", str, __LINE__, __FILE__, \ + code, strerror(code)); \ + abort(); \ + } while (0) -/** Exits without invoking destructors (via _exit), useful for code after fork. */ +/// Exits without invoking destructors (via _exit), useful for code after fork. void exit_without_destructors(int code) __attribute__((noreturn)); -/** - Save the shell mode on startup so we can restore them on exit -*/ +/// Save the shell mode on startup so we can restore them on exit. extern struct termios shell_modes; -/** - The character to use where the text has been truncated. Is an - ellipsis on unicode system and a $ on other systems. -*/ +/// The character to use where the text has been truncated. Is an ellipsis on unicode system and a $ +/// on other systems. extern wchar_t ellipsis_char; -/* Character representing an omitted newline at the end of text */ +/// Character representing an omitted newline at the end of text. extern wchar_t omitted_newline_char; -/** - The verbosity level of fish. If a call to debug has a severity - level higher than \c debug_level, it will not be printed. -*/ +/// The verbosity level of fish. If a call to debug has a severity level higher than \c debug_level, +/// it will not be printed. extern int debug_level; -/** - Profiling flag. True if commands should be profiled. -*/ +/// Profiling flag. True if commands should be profiled. extern bool g_profiling_active; -/** - Name of the current program. Should be set at startup. Used by the - debug function. -*/ +/// Name of the current program. Should be set at startup. Used by the debug function. extern const wchar_t *program_name; -/* Variants of read() and write() that ignores return values, defeating a warning */ +// Variants of read() and write() that ignores return values, defeating a warning. void read_ignore(int fd, void *buff, size_t count); void write_ignore(int fd, const void *buff, size_t count); -/** - This macro is used to check that an input argument is not null. It - is a bit lika a non-fatal form of assert. Instead of exit-ing on - failure, the current function is ended at once. The second - parameter is the return value of the current function on failure. -*/ -#define CHECK( arg, retval ) \ - if (!(arg)) \ - { \ - debug( 0, \ - "function %s called with null value for argument %s. ", \ - __func__, \ - #arg ); \ - bugreport(); \ - show_stackframe(); \ - return retval; \ - } +/// This macro is used to check that an input argument is not null. It is a bit lika a non-fatal +/// form of assert. Instead of exit-ing on failure, the current function is ended at once. The +/// second parameter is the return value of the current function on failure. +#define CHECK(arg, retval) \ + if (!(arg)) { \ + debug(0, "function %s called with null value for argument %s. ", __func__, #arg); \ + bugreport(); \ + show_stackframe(); \ + return retval; \ + } -/** - Pause for input, then exit the program. If supported, print a backtrace first. -*/ -#define FATAL_EXIT() \ - { \ - char exit_read_buff; \ - show_stackframe(); \ - read_ignore( 0, &exit_read_buff, 1 ); \ - exit_without_destructors( 1 ); \ - } \ - +/// Pause for input, then exit the program. If supported, print a backtrace first. +#define FATAL_EXIT() \ + { \ + char exit_read_buff; \ + show_stackframe(); \ + read_ignore(0, &exit_read_buff, 1); \ + exit_without_destructors(1); \ + } -/** - Exit program at once, leaving an error message about running out of memory. -*/ -#define DIE_MEM() \ - { \ - fwprintf( stderr, \ - L"fish: Out of memory on line %ld of file %s, shutting down fish\n", \ - (long)__LINE__, \ - __FILE__ ); \ - FATAL_EXIT(); \ - } +/// Exit program at once, leaving an error message about running out of memory. +#define DIE_MEM() \ + { \ + fwprintf(stderr, L"fish: Out of memory on line %ld of file %s, shutting down fish\n", \ + (long)__LINE__, __FILE__); \ + FATAL_EXIT(); \ + } -/** - Check if signals are blocked. If so, print an error message and - return from the function performing this check. -*/ -#define CHECK_BLOCK(retval) \ - if (signal_is_blocked()) \ - { \ - debug( 0, \ - "function %s called while blocking signals. ", \ - __func__); \ - bugreport(); \ - show_stackframe(); \ - return retval; \ - } +/// Check if signals are blocked. If so, print an error message and return from the function +/// performing this check. +#define CHECK_BLOCK(retval) \ + if (signal_is_blocked()) { \ + debug(0, "function %s called while blocking signals. ", __func__); \ + bugreport(); \ + show_stackframe(); \ + return retval; \ + } -/** - Shorthand for wgettext call -*/ +/// Shorthand for wgettext call. #define _(wstr) wgettext(wstr) -/** - Noop, used to tell xgettext that a string should be translated, - even though it is not directly sent to wgettext. -*/ +/// Noop, used to tell xgettext that a string should be translated, even though it is not directly +/// sent to wgettext. #define N_(wstr) wstr -/** - Check if the specified string element is a part of the specified string list - */ -#define contains( str, ... ) contains_internal( str, 0, __VA_ARGS__, NULL ) +/// Check if the specified string element is a part of the specified string list. +#define contains(str, ...) contains_internal(str, 0, __VA_ARGS__, NULL) -/** - Print a stack trace to stderr -*/ +/// Print a stack trace to stderr. void show_stackframe(); - -/** - Read a line from the stream f into the string. Returns - the number of bytes read or -1 on failure. - - If the carriage return character is encountered, it is - ignored. fgetws() considers the line to end if reading the file - results in either a newline (L'\n') character, the null (L'\\0') - character or the end of file (WEOF) character. -*/ +/// Read a line from the stream f into the string. Returns the number of bytes read or -1 on +/// failure. +/// +/// If the carriage return character is encountered, it is ignored. fgetws() considers the line to +/// end if reading the file results in either a newline (L'\n') character, the null (L'\\0') +/// character or the end of file (WEOF) character. int fgetws2(wcstring *s, FILE *f); - -/** - Returns a 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. - */ +/// Returns a 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 char *in, size_t len); wcstring str2wcstring(const std::string &in); -/** - Returns a newly allocated multibyte character string equivalent of - the specified wide character string - - This function decodes illegal character sequences in a reversible - way using the private use area. -*/ +/// Returns a newly allocated multibyte character string equivalent of the specified wide character +/// string. +/// +/// This function decodes illegal character sequences in a reversible way using the private use +/// area. char *wcs2str(const wchar_t *in); char *wcs2str(const wcstring &in); std::string wcs2string(const wcstring &input); -/** Test if a string prefixes another. Returns true if a is a prefix of b */ +/// Test if a string prefixes another. Returns true if a is a prefix of b. bool string_prefixes_string(const wcstring &proposed_prefix, const wcstring &value); bool string_prefixes_string(const wchar_t *proposed_prefix, const wcstring &value); -/** Test if a string is a suffix of another */ +/// Test if a string is a suffix of another. bool string_suffixes_string(const wcstring &proposed_suffix, const wcstring &value); bool string_suffixes_string(const wchar_t *proposed_suffix, const wcstring &value); -/** Test if a string prefixes another without regard to case. Returns true if a is a prefix of b */ -bool string_prefixes_string_case_insensitive(const wcstring &proposed_prefix, const wcstring &value); +/// Test if a string prefixes another without regard to case. Returns true if a is a prefix of b. +bool string_prefixes_string_case_insensitive(const wcstring &proposed_prefix, + const wcstring &value); -enum fuzzy_match_type_t -{ - /* We match the string exactly: FOOBAR matches FOOBAR */ +enum fuzzy_match_type_t { + // We match the string exactly: FOOBAR matches FOOBAR. fuzzy_match_exact = 0, - /* We match a prefix of the string: FO matches FOOBAR */ + // We match a prefix of the string: FO matches FOOBAR. fuzzy_match_prefix, - /* We match the string exactly, but in a case insensitive way: foobar matches FOOBAR */ + // We match the string exactly, but in a case insensitive way: foobar matches FOOBAR. fuzzy_match_case_insensitive, - /* We match a prefix of the string, in a case insensitive way: foo matches FOOBAR */ + // We match a prefix of the string, in a case insensitive way: foo matches FOOBAR. fuzzy_match_prefix_case_insensitive, - /* We match a substring of the string: OOBA matches FOOBAR */ + // We match a substring of the string: OOBA matches FOOBAR. fuzzy_match_substring, - /* A subsequence match with insertions only: FBR matches FOOBAR */ + // A subsequence match with insertions only: FBR matches FOOBAR. fuzzy_match_subsequence_insertions_only, - /* We don't match the string */ + // We don't match the string. fuzzy_match_none }; -/* Indicates where a match type requires replacing the entire token */ -static inline bool match_type_requires_full_replacement(fuzzy_match_type_t t) -{ - switch (t) - { +/// Indicates where a match type requires replacing the entire token. +static inline bool match_type_requires_full_replacement(fuzzy_match_type_t t) { + switch (t) { case fuzzy_match_exact: - case fuzzy_match_prefix: + case fuzzy_match_prefix: { return false; - default: - return true; + } + default: { return true; } } } -/* Indicates where a match shares a prefix with the string it matches */ -static inline bool match_type_shares_prefix(fuzzy_match_type_t t) -{ - switch (t) - { +/// Indicates where a match shares a prefix with the string it matches. +static inline bool match_type_shares_prefix(fuzzy_match_type_t t) { + switch (t) { case fuzzy_match_exact: case fuzzy_match_prefix: case fuzzy_match_case_insensitive: - case fuzzy_match_prefix_case_insensitive: + case fuzzy_match_prefix_case_insensitive: { return true; - default: - return false; + } + default: { return false; } } } -/** Test if string is a fuzzy match to another */ -struct string_fuzzy_match_t -{ +/// Test if string is a fuzzy match to another. +struct string_fuzzy_match_t { enum fuzzy_match_type_t type; - /* Strength of the match. The value depends on the type. Lower is stronger. */ + // Strength of the match. The value depends on the type. Lower is stronger. size_t match_distance_first; size_t match_distance_second; - /* Constructor */ - explicit string_fuzzy_match_t(enum fuzzy_match_type_t t, size_t distance_first = 0, size_t distance_second = 0); + // Constructor. + explicit string_fuzzy_match_t(enum fuzzy_match_type_t t, size_t distance_first = 0, + size_t distance_second = 0); - /* Return -1, 0, 1 if this match is (respectively) better than, equal to, or worse than rhs */ + // Return -1, 0, 1 if this match is (respectively) better than, equal to, or worse than rhs. int compare(const string_fuzzy_match_t &rhs) const; }; -/* Compute a fuzzy match for a string. If maximum_match is not fuzzy_match_none, limit the type to matches at or below that type. */ -string_fuzzy_match_t string_fuzzy_match_string(const wcstring &string, const wcstring &match_against, fuzzy_match_type_t limit_type = fuzzy_match_none); +/// Compute a fuzzy match for a string. If maximum_match is not fuzzy_match_none, limit the type to +/// matches at or below that type. +string_fuzzy_match_t string_fuzzy_match_string(const wcstring &string, + const wcstring &match_against, + fuzzy_match_type_t limit_type = fuzzy_match_none); - -/** Test if a list contains a string using a linear search. */ +/// Test if a list contains a string using a linear search. bool list_contains_string(const wcstring_list_t &list, const wcstring &str); - void assert_is_main_thread(const char *who); #define ASSERT_IS_MAIN_THREAD_TRAMPOLINE(x) assert_is_main_thread(x) #define ASSERT_IS_MAIN_THREAD() ASSERT_IS_MAIN_THREAD_TRAMPOLINE(__FUNCTION__) @@ -401,185 +353,158 @@ void assert_is_background_thread(const char *who); #define ASSERT_IS_BACKGROUND_THREAD_TRAMPOLINE(x) assert_is_background_thread(x) #define ASSERT_IS_BACKGROUND_THREAD() ASSERT_IS_BACKGROUND_THREAD_TRAMPOLINE(__FUNCTION__) -/* Useful macro for asserting that a lock is locked. This doesn't check whether this thread locked it, which it would be nice if it did, but here it is anyways. */ +/// Useful macro for asserting that a lock is locked. This doesn't check whether this thread locked +/// it, which it would be nice if it did, but here it is anyways. void assert_is_locked(void *mutex, const char *who, const char *caller); #define ASSERT_IS_LOCKED(x) assert_is_locked((void *)(&x), #x, __FUNCTION__) -/** Format the specified size (in bytes, kilobytes, etc.) into the specified stringbuffer. */ +/// Format the specified size (in bytes, kilobytes, etc.) into the specified stringbuffer. wcstring format_size(long long sz); -/** Version of format_size that does not allocate memory. */ +/// Version of format_size that does not allocate memory. void format_size_safe(char buff[128], unsigned long long sz); -/** Our crappier versions of debug which is guaranteed to not allocate any memory, or do anything other than call write(). This is useful after a call to fork() with threads. */ -void debug_safe(int level, const char *msg, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL, const char *param5 = NULL, const char *param6 = NULL, const char *param7 = NULL, const char *param8 = NULL, const char *param9 = NULL, const char *param10 = NULL, const char *param11 = NULL, const char *param12 = NULL); +/// Our crappier versions of debug which is guaranteed to not allocate any memory, or do anything +/// other than call write(). This is useful after a call to fork() with threads. +void debug_safe(int level, const char *msg, const char *param1 = NULL, const char *param2 = NULL, + const char *param3 = NULL, const char *param4 = NULL, const char *param5 = NULL, + const char *param6 = NULL, const char *param7 = NULL, const char *param8 = NULL, + const char *param9 = NULL, const char *param10 = NULL, const char *param11 = NULL, + const char *param12 = NULL); -/** Writes out a long safely */ +/// Writes out a long safely. void format_long_safe(char buff[64], long val); void format_long_safe(wchar_t buff[64], long val); -/** "Narrows" a wide character string. This just grabs any ASCII characters and trunactes. */ +/// "Narrows" a wide character string. This just grabs any ASCII characters and trunactes. void narrow_string_safe(char buff[64], const wchar_t *s); - -template -T from_string(const wcstring &x) -{ +template +T from_string(const wcstring &x) { T result; std::wstringstream stream(x); stream >> result; return result; } -template -T from_string(const std::string &x) -{ +template +T from_string(const std::string &x) { T result = T(); std::stringstream stream(x); stream >> result; return result; } -template -wcstring to_string(const T &x) -{ +template +wcstring to_string(const T &x) { std::wstringstream stream; stream << x; return stream.str(); } -/* wstringstream is a huge memory pig. Let's provide some specializations where we can. */ -template<> -inline wcstring to_string(const long &x) -{ +// wstringstream is a huge memory pig. Let's provide some specializations where we can. +template <> +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) -{ - return ! x.empty() && strchr("YTyt1", x.at(0)); +template <> +inline bool from_string(const std::string &x) { + return !x.empty() && strchr("YTyt1", x.at(0)); } -template<> -inline bool from_string(const wcstring &x) -{ - return ! x.empty() && wcschr(L"YTyt1", x.at(0)); +template <> +inline bool from_string(const wcstring &x) { + return !x.empty() && wcschr(L"YTyt1", x.at(0)); } -template<> -inline wcstring to_string(const int &x) -{ +template <> +inline wcstring to_string(const int &x) { return to_string(static_cast(x)); } -/* A hackish thing to simulate rvalue references in C++98. - The idea is that you can define a constructor to take a moved_ref and then swap() out of it. - */ -template -struct moved_ref -{ +// A hackish thing to simulate rvalue references in C++98. The idea is that you can define a +// constructor to take a moved_ref and then swap() out of it. +template +struct moved_ref { T &val; - - explicit moved_ref(T &v) : val(v) { } + + explicit moved_ref(T &v) : val(v) {} }; wchar_t **make_null_terminated_array(const wcstring_list_t &lst); char **make_null_terminated_array(const std::vector &lst); -/* Helper class for managing a null-terminated array of null-terminated strings (of some char type) */ +// 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; - /* No assignment or copying */ + // No assignment or copying. void operator=(null_terminated_array_t rhs); null_terminated_array_t(const null_terminated_array_t &); typedef std::vector > string_list_t; - size_t size() const - { + size_t size() const { size_t len = 0; - if (array != NULL) - { - while (array[len] != NULL) - { + if (array != NULL) { + while (array[len] != NULL) { len++; } } return len; } - void free(void) - { + void free(void) { ::free((void *)array); array = NULL; } -public: - null_terminated_array_t() : array(NULL) { } - explicit null_terminated_array_t(const string_list_t &argv) : array(make_null_terminated_array(argv)) - { - } + public: + null_terminated_array_t() : array(NULL) {} + explicit null_terminated_array_t(const string_list_t &argv) + : array(make_null_terminated_array(argv)) {} - ~null_terminated_array_t() - { - this->free(); - } + ~null_terminated_array_t() { this->free(); } - void set(const string_list_t &argv) - { + void set(const string_list_t &argv) { this->free(); this->array = make_null_terminated_array(argv); } - const CharType_t * const *get() const - { - return array; - } + const CharType_t *const *get() const { return array; } - void clear() - { - this->free(); - } + void clear() { this->free(); } }; -/* Helper function to convert from a null_terminated_array_t to a null_terminated_array_t */ -void convert_wide_array_to_narrow(const null_terminated_array_t &arr, null_terminated_array_t *output); +// Helper function to convert from a null_terminated_array_t to a +// null_terminated_array_t. +void convert_wide_array_to_narrow(const null_terminated_array_t &arr, + null_terminated_array_t *output); bool is_forked_child(); - -class mutex_lock_t -{ - public: +class mutex_lock_t { + public: pthread_mutex_t mutex; - mutex_lock_t() - { - VOMIT_ON_FAILURE_NO_ERRNO(pthread_mutex_init(&mutex, NULL)); - } - - ~mutex_lock_t() - { - VOMIT_ON_FAILURE_NO_ERRNO(pthread_mutex_destroy(&mutex)); - } + mutex_lock_t() { VOMIT_ON_FAILURE_NO_ERRNO(pthread_mutex_init(&mutex, NULL)); } + + ~mutex_lock_t() { VOMIT_ON_FAILURE_NO_ERRNO(pthread_mutex_destroy(&mutex)); } }; -/* Basic scoped lock class */ -class scoped_lock -{ +// Basic scoped lock class. +class scoped_lock { pthread_mutex_t *lock_obj; bool locked; - /* No copying */ + // No copying. scoped_lock &operator=(const scoped_lock &); scoped_lock(const scoped_lock &); -public: + public: void lock(void); void unlock(void); explicit scoped_lock(pthread_mutex_t &mutex); @@ -587,108 +512,79 @@ class scoped_lock ~scoped_lock(); }; -class rwlock_t -{ -public: +class rwlock_t { + public: pthread_rwlock_t rwlock; - rwlock_t() - { - VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_init(&rwlock, NULL)); - } + rwlock_t() { VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_init(&rwlock, NULL)); } - ~rwlock_t() - { - VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_destroy(&rwlock)); - } + ~rwlock_t() { VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_destroy(&rwlock)); } }; -/* - Scoped lock class for rwlocks - */ -class scoped_rwlock -{ +// Scoped lock class for rwlocks. +class scoped_rwlock { pthread_rwlock_t *rwlock_obj; bool locked; bool locked_shared; - /* No copying */ + // No copying. scoped_rwlock &operator=(const scoped_lock &); explicit scoped_rwlock(const scoped_lock &); -public: + public: void lock(void); void unlock(void); void lock_shared(void); void unlock_shared(void); - /* - upgrade shared lock to exclusive. - equivalent to `lock.unlock_shared(); lock.lock();` - */ + // Upgrade shared lock to exclusive. Equivalent to `lock.unlock_shared(); lock.lock();`. void upgrade(void); explicit scoped_rwlock(pthread_rwlock_t &rwlock, bool shared = false); explicit scoped_rwlock(rwlock_t &rwlock, bool shared = false); ~scoped_rwlock(); }; -/** - A scoped manager to save the current value of some variable, and optionally - set it to a new value. On destruction it restores the variable to its old - value. - - This can be handy when there are multiple code paths to exit a block. -*/ +/// A scoped manager to save the current value of some variable, and optionally set it to a new +/// value. On destruction it restores the variable to its old value. +/// +/// This can be handy when there are multiple code paths to exit a block. template -class scoped_push -{ - T * const ref; +class scoped_push { + T *const ref; T saved_value; bool restored; -public: - explicit scoped_push(T *r): ref(r), saved_value(*r), restored(false) - { - } + public: + explicit scoped_push(T *r) : ref(r), saved_value(*r), restored(false) {} - scoped_push(T *r, const T &new_value) : ref(r), saved_value(*r), restored(false) - { + scoped_push(T *r, const T &new_value) : ref(r), saved_value(*r), restored(false) { *r = new_value; } - ~scoped_push() - { - restore(); - } + ~scoped_push() { restore(); } - void restore() - { - if (!restored) - { + void restore() { + if (!restored) { std::swap(*ref, saved_value); restored = true; } } }; - -/* Wrapper around wcstok */ -class wcstokenizer -{ +/// Wrapper around wcstok. +class wcstokenizer { wchar_t *buffer, *str, *state; const wcstring sep; - /* No copying */ + // No copying. wcstokenizer &operator=(const wcstokenizer &); wcstokenizer(const wcstokenizer &); -public: + public: wcstokenizer(const wcstring &s, const wcstring &separator); bool next(wcstring &result); ~wcstokenizer(); }; -/** - Appends a path component, with a / if necessary - */ +/// Appends a path component, with a / if necessary. void append_path_component(wcstring &path, const wcstring &component); wcstring format_string(const wchar_t *format, ...); @@ -696,237 +592,176 @@ wcstring vformat_string(const wchar_t *format, va_list va_orig); void append_format(wcstring &str, const wchar_t *format, ...); void append_formatv(wcstring &str, const wchar_t *format, va_list ap); -/** - 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 -*/ - +/// 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. const wchar_t *wcsvarname(const wchar_t *str); const wchar_t *wcsvarname(const wcstring &str); - -/** - 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 -*/ - +/// 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 wcstring &str); -/** - Test if the given string is valid in a variable name - - \return true if this is a valid name, false otherwise -*/ - +/// Test if the given string is valid in a variable name. +/// +/// \return true if this is a valid name, false otherwise. bool wcsvarchr(wchar_t chr); -/** - Convenience variants on fish_wcwswidth(). - - See fallback.h for the normal definitions. -*/ +/// Convenience variants on fish_wcwswidth(). +/// +/// See fallback.h for the normal definitions. int fish_wcswidth(const wchar_t *str); -int fish_wcswidth(const wcstring& str); +int fish_wcswidth(const wcstring &str); -/** - This functions returns the end of the quoted substring beginning at - \c in. The type of quoting character is detemrined by examining \c - in. Returns 0 on error. - - \param in the position of the opening quote -*/ +/// This functions returns the end of the quoted substring beginning at \c in. The type of quoting +/// character is detemrined by examining \c in. Returns 0 on error. +/// +/// \param in the position of the opening quote. wchar_t *quote_end(const wchar_t *in); -/** - A call to this function will reset the error counter. Some - functions print out non-critical error messages. These should check - the error_count before, and skip printing the message if - MAX_ERROR_COUNT messages have been printed. The error_reset() - should be called after each interactive command executes, to allow - new messages to be printed. -*/ +/// A call to this function will reset the error counter. Some functions print out non-critical +/// error messages. These should check the error_count before, and skip printing the message if +/// MAX_ERROR_COUNT messages have been printed. The error_reset() should be called after each +/// interactive command executes, to allow new messages to be printed. 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 '$'. -*/ +/// 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 '$'. 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 - - \return zero if needle is not found, of if needle is null, non-zero otherwise -*/ +/// 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. +/// +/// \return zero if needle is not found, of if needle is null, non-zero otherwise. __sentinel bool contains_internal(const wchar_t *needle, int vararg_handle, ...); __sentinel bool contains_internal(const wcstring &needle, int vararg_handle, ...); -/** - Call read while blocking the SIGCHLD signal. Should only be called - if you _know_ there is data available for reading, or the program - will hang until there is data. -*/ +/// Call read while blocking the SIGCHLD signal. Should only be called if you _know_ there is data +/// available for reading, or the program will hang until there is data. long read_blocked(int fd, void *buf, size_t count); -/** - Loop a write request while failure is non-critical. Return -1 and set errno - in case of critical error. - */ +/// Loop a write request while failure is non-critical. Return -1 and set errno in case of critical +/// error. ssize_t write_loop(int fd, const char *buff, size_t count); -/** - Loop a read request while failure is non-critical. Return -1 and set errno - in case of critical error. - */ +/// Loop a read request while failure is non-critical. Return -1 and set errno in case of critical +/// error. ssize_t read_loop(int fd, void *buff, size_t count); - -/** - Issue a debug message with printf-style string formating and - automatic line breaking. The string will begin with the string \c - program_name, followed by a colon and a whitespace. - - 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. - - Example: - - debug( 1, L"Pi = %.3f", M_PI ); - - will print the string 'fish: Pi = 3.141', given that debug_level is 1 or higher, and that program_name is 'fish'. -*/ +/// Issue a debug message with printf-style string formating and automatic line breaking. The string +/// will begin with the string \c program_name, followed by a colon and a whitespace. +/// +/// 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. +/// +/// Example: +/// +/// debug( 1, L"Pi = %.3f", M_PI ); +/// +/// 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, ...); -/** - Replace special characters with backslash escape sequences. Newline is - replaced with \n, etc. - - \param in The string to be escaped - \param flags Flags to control the escaping - \return The escaped string -*/ - +/// Replace special characters with backslash escape sequences. Newline is replaced with \n, etc. +/// +/// \param in The string to be escaped +/// \param flags Flags to control the escaping +/// \return The escaped string wcstring 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 - counterparts. Also optionally change the wildcards, the tilde - character and a few more into constants which are defined in a - private use area of Unicode. This assumes wchar_t is a unicode - character set. -*/ +/// Expand backslashed escapes and substitute them with their unescaped counterparts. Also +/// optionally change the wildcards, the tilde character and a few more into constants which are +/// defined in a private use area of Unicode. This assumes wchar_t is a unicode character set. -/** Given a null terminated string starting with a backslash, read the escape as if it is unquoted, appending to result. Return the number of characters consumed, or 0 on error */ -size_t read_unquoted_escape(const wchar_t *input, wcstring *result, bool allow_incomplete, bool unescape_special); +/// Given a null terminated string starting with a backslash, read the escape as if it is unquoted, +/// appending to result. Return the number of characters consumed, or 0 on error. +size_t read_unquoted_escape(const wchar_t *input, wcstring *result, bool allow_incomplete, + bool unescape_special); -/** Unescapes a string in-place. A true result indicates the string was unescaped, a false result indicates the string was unmodified. */ +/// Unescapes a string in-place. A true result indicates the string was unescaped, a false result +/// indicates the string was unmodified. bool unescape_string_in_place(wcstring *str, unescape_flags_t escape_special); -/** Unescapes a string, returning the unescaped value by reference. On failure, the output is set to an empty string. */ +/// Unescapes a string, returning the unescaped value by reference. On failure, the output is set to +/// an empty string. bool unescape_string(const wchar_t *input, wcstring *output, unescape_flags_t escape_special); bool unescape_string(const wcstring &input, wcstring *output, unescape_flags_t 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. - - Only works if common_handle_winch is registered to handle winch signals. -*/ +/// Returns the width of the terminal window, so that not all functions that use these values +/// continually have to keep track of it separately. +/// +/// Only works if common_handle_winch is registered to handle winch signals. int common_get_width(); -/** - Returns the height of the terminal window, so that not all - functions that use these values continually have to keep track of - it separatly. - Only works if common_handle_winch is registered to handle winch signals. -*/ +/// Returns the height of the terminal window, so that not all functions that use these values +/// continually have to keep track of it separatly. +/// +/// Only works if common_handle_winch is registered to handle winch signals. int common_get_height(); -/** - Handle a window change event by looking up the new window size and - saving it in an internal variable used by common_get_wisth and - common_get_height(). -*/ +/// Handle a window change event by looking up the new window size and saving it in an internal +/// variable used by common_get_wisth and common_get_height(). void common_handle_winch(int signal); -/** - Write the given paragraph of output, redoing linebreaks to fit - the current screen. -*/ +/// Write the given paragraph of output, redoing linebreaks to fit the current screen. wcstring reformat_for_screen(const wcstring &msg); -/** - 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. -*/ +/// 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. void tokenize_variable_array(const wcstring &val, wcstring_list_t &out); -/** - Make sure the specified direcotry exists. If needed, try to create - it and any currently not existing parent directories.. - - \return 0 if, at the time of function return the directory exists, -1 otherwise. -*/ +/// Make sure the specified direcotry exists. If needed, try to create it and any currently not +/// existing parent directories. +/// +/// \return 0 if, at the time of function return the directory exists, -1 otherwise. int create_directory(const wcstring &d); -/** - Print a short message about how to file a bug report to stderr -*/ +/// Print a short message about how to file a bug report to stderr. void bugreport(); -/** - Return the number of seconds from the UNIX epoch, with subsecond - precision. This function uses the gettimeofday function, and will - have the same precision as that function. - - If an error occurs, NAN is returned. - */ +/// Return the number of seconds from the UNIX epoch, with subsecond precision. This function uses +/// the gettimeofday function, and will have the same precision as that function. If an error +/// occurs, NAN is returned. double timef(); -/** - Call the following function early in main to set the main thread. - This is our replacement for pthread_main_np(). - */ +/// Call the following function early in main to set the main thread. This is our replacement for +/// pthread_main_np(). void set_main_thread(); bool is_main_thread(); -/** Configures thread assertions for testing */ +/// Configures thread assertions for testing. void configure_thread_assertions_for_testing(); -/** Set up a guard to complain if we try to do certain things (like take a lock) after calling fork */ +/// Set up a guard to complain if we try to do certain things (like take a lock) after calling fork. void setup_fork_guards(void); -/** Save the value of tcgetpgrp so we can restore it on exit */ +/// Save the value of tcgetpgrp so we can restore it on exit. void save_term_foreground_process_group(void); void restore_term_foreground_process_group(void); -/** Return whether we are the child of a fork */ +/// Return whether we are the child of a fork. bool is_forked_child(void); void assert_is_not_forked_child(const char *who); #define ASSERT_IS_NOT_FORKED_CHILD_TRAMPOLINE(x) assert_is_not_forked_child(x) #define ASSERT_IS_NOT_FORKED_CHILD() ASSERT_IS_NOT_FORKED_CHILD_TRAMPOLINE(__FUNCTION__) -/** Macro to help suppress potentially unused variable warnings */ +/// Macro to help suppress potentially unused variable warnings. #define USE(var) (void)(var) extern "C" { - __attribute__((noinline)) void debug_thread_error(void); +__attribute__((noinline)) void debug_thread_error(void); } - #endif From 75064ed786ed41f962ac146a89102b4ce71f4e2f Mon Sep 17 00:00:00 2001 From: David Adam Date: Thu, 28 Apr 2016 08:20:58 +0800 Subject: [PATCH 148/363] fish.spec: hostname dependency for openSUSE As discussed in https://github.com/fish-shell/fish-shell/commit/ed4b78918a944fe3b3a1e05ac553e89d5f1fa411 [ci skip] --- fish.spec.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fish.spec.in b/fish.spec.in index 04006037d..89ce0314b 100644 --- a/fish.spec.in +++ b/fish.spec.in @@ -18,7 +18,7 @@ Requires: python Requires: which Requires: man -%if 0%{?rhel_version} >= 700 || 0%{?centos_version} >= 700 || 0%{?fedora} +%if 0%{?rhel_version} >= 700 || 0%{?centos_version} >= 700 || 0%{?fedora} || 0%{?is_opensuse} Requires: hostname %else Requires: net-tools From ce41e3468ec77acb958de244f86ed65f6c5cbedf Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sat, 16 Apr 2016 20:03:14 -0700 Subject: [PATCH 149/363] add way to comment/uncomment a command Fixes #2375 (cherry picked from commit 2f8d0e9aba3662fcb1032ffe33bc6faf2e04eb8f) --- .../__fish_toggle_comment_commandline.fish | 17 +++++++++++++++++ share/functions/fish_default_key_bindings.fish | 5 ++++- share/functions/fish_vi_key_bindings.fish | 8 ++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 share/functions/__fish_toggle_comment_commandline.fish diff --git a/share/functions/__fish_toggle_comment_commandline.fish b/share/functions/__fish_toggle_comment_commandline.fish new file mode 100644 index 000000000..8bdd7e822 --- /dev/null +++ b/share/functions/__fish_toggle_comment_commandline.fish @@ -0,0 +1,17 @@ +# This is meant to be bound to key sequences such as \e#. It provides a simple way to quickly +# comment/uncomment the current command. This is something introduced by the Korn shell (ksh) in +# 1993. It allows you to capture a command in the shell history without executing it. Then +# retrieving the command from the shell history and removing the comment chars. +# +# This deliberately does not execute the command when removing the comment characters to give you an +# opportunity to modify the command. + +function __fish_toggle_comment_commandline --description 'Comment/uncomment the current command' + set -l cmdlines (commandline -b) + if test "$cmdlines" = "" + return + end + set -l cmdlines (printf '%s\n' '#'$cmdlines | string replace -r '^##' '') + commandline -r $cmdlines + string match -q '#*' $cmdlines[1]; and commandline -f execute +end diff --git a/share/functions/fish_default_key_bindings.fish b/share/functions/fish_default_key_bindings.fish index 8d86c7eec..c65cd49ea 100644 --- a/share/functions/fish_default_key_bindings.fish +++ b/share/functions/fish_default_key_bindings.fish @@ -144,5 +144,8 @@ function fish_default_key_bindings -d "Default (Emacs-like) key bindings for fis bind $argv \eOc forward-word bind $argv \eOd backward-word end -end + # Make it easy to turn an unexecuted command into a comment in the shell history. Also, + # remove the commenting chars so the command can be further edited then executed. + bind \e\# __fish_toggle_comment_commandline +end diff --git a/share/functions/fish_vi_key_bindings.fish b/share/functions/fish_vi_key_bindings.fish index f63956677..2518ab74c 100644 --- a/share/functions/fish_vi_key_bindings.fish +++ b/share/functions/fish_vi_key_bindings.fish @@ -224,4 +224,12 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' bind -M visual -m default \e end-selection force-repaint set fish_bind_mode $init_mode + + # Make it easy to turn an unexecuted command into a comment in the shell history. Also, remove + # the commenting chars so the command can be further edited then executed. + bind -M default \# __fish_toggle_comment_commandline + bind -M visual \# __fish_toggle_comment_commandline + bind -M default \e\# __fish_toggle_comment_commandline + bind -M insert \e\# __fish_toggle_comment_commandline + bind -M visual \e\# __fish_toggle_comment_commandline end From 96a28df0183a860ca007eb84a8e46d43d9efe7b7 Mon Sep 17 00:00:00 2001 From: Cody Scott Date: Thu, 28 Apr 2016 15:26:45 -0400 Subject: [PATCH 150/363] switch to newer flag --set-upstream-to for git (#2982) set-upstream was deprecated in git 1.8.0 as stated in [1] in favor of set-upstream-to. this patch replaces the old flag in fish completions [1]: https://raw.githubusercontent.com/git/git/master/Documentation/RelNotes/1.8.0.txt --- share/completions/git.fish | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index 82ea0e8bf..cea4a9bf0 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -286,7 +286,7 @@ complete -f -c git -n '__fish_git_using_command branch' -s M -d 'Force renaming complete -f -c git -n '__fish_git_using_command branch' -s a -d 'Lists both local and remote branches' complete -f -c git -n '__fish_git_using_command branch' -s t -l track -d 'Track remote branch' complete -f -c git -n '__fish_git_using_command branch' -l no-track -d 'Do not track remote branch' -complete -f -c git -n '__fish_git_using_command branch' -l set-upstream -d 'Set remote branch to track' +complete -f -c git -n '__fish_git_using_command branch' -l set-upstream-to -d 'Set remote branch to track' complete -f -c git -n '__fish_git_using_command branch' -l merged -d 'List branches that have been merged' complete -f -c git -n '__fish_git_using_command branch' -l no-merged -d 'List branches that have not been merged' @@ -410,7 +410,7 @@ complete -f -c git -n '__fish_git_using_command push' -l tags -d 'Push all refs complete -f -c git -n '__fish_git_using_command push' -s n -l dry-run -d 'Do everything except actually send the updates' complete -f -c git -n '__fish_git_using_command push' -l porcelain -d 'Produce machine-readable output' complete -f -c git -n '__fish_git_using_command push' -s f -l force -d 'Force update of remote refs' -complete -f -c git -n '__fish_git_using_command push' -s u -l set-upstream -d 'Add upstream (tracking) reference' +complete -f -c git -n '__fish_git_using_command push' -s u -l set-upstream-to -d 'Add upstream (tracking) reference' complete -f -c git -n '__fish_git_using_command push' -s q -l quiet -d 'Be quiet' complete -f -c git -n '__fish_git_using_command push' -s v -l verbose -d 'Be verbose' complete -f -c git -n '__fish_git_using_command push' -l progress -d 'Force progress status' From 5fa8370c133738aa21a3b4a7d3a89f9188068498 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 28 Apr 2016 21:30:26 +0200 Subject: [PATCH 151/363] git completion: Only show unmerged branches for cherry-pick --- share/completions/git.fish | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index cea4a9bf0..af9dc685d 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -11,13 +11,13 @@ function __fish_git_commits end function __fish_git_branches - command git branch --no-color -a ^/dev/null | string match -r -v ' -> ' | string trim -c "* " | string replace -r "^remotes/" "" + command git branch --no-color -a $argv ^/dev/null | string match -r -v ' -> ' | string trim -c "* " | string replace -r "^remotes/" "" end function __fish_git_unique_remote_branches # Allow all remote branches with one remote without the remote part # This is useful for `git checkout` to automatically create a remote-tracking branch - command git branch --no-color -a ^/dev/null | string match -r -v ' -> ' | string trim -c "* " | string replace -r "^remotes/[^/]*/" "" | sort | uniq -u + command git branch --no-color -a $argv ^/dev/null | string match -r -v ' -> ' | string trim -c "* " | string replace -r "^remotes/[^/]*/" "" | sort | uniq -u end function __fish_git_tags @@ -292,8 +292,8 @@ complete -f -c git -n '__fish_git_using_command branch' -l no-merged -d 'List br ### cherry-pick complete -f -c git -n '__fish_git_needs_command' -a cherry-pick -d 'Apply the change introduced by an existing commit' -complete -f -c git -n '__fish_git_using_command cherry-pick' -a '(__fish_git_branches)' -d 'Branch' -complete -f -c git -n '__fish_git_using_command cherry-pick' -a '(__fish_git_unique_remote_branches)' -d 'Remote branch' +complete -f -c git -n '__fish_git_using_command cherry-pick' -a '(__fish_git_branches --no-merged)' -d 'Branch' +complete -f -c git -n '__fish_git_using_command cherry-pick' -a '(__fish_git_unique_remote_branches --no-merged)' -d 'Remote branch' complete -f -c git -n '__fish_git_using_command cherry-pick' -s e -l edit -d 'Edit the commit message prior to committing' complete -f -c git -n '__fish_git_using_command cherry-pick' -s x -d 'Append info in generated commit on the origin of the cherry-picked change' complete -f -c git -n '__fish_git_using_command cherry-pick' -s n -l no-commit -d 'Apply changes without making any commit' From 6c329e8a839c1b5eeaf2e545b4f4084c3a8830f7 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Wed, 13 Apr 2016 17:14:50 -0700 Subject: [PATCH 152/363] provide a realpath implementation Not all distros have a `realpath` command. Provide a function that uses the real command if available else use the fish builtin. Fixes #2932 --- doc_src/fish_realpath.txt | 13 ++++++++++++ share/functions/realpath.fish | 13 ++++++++++++ src/builtin.cpp | 29 ++++++++++++++++++++++++++ tests/fish_realpath.err | 3 +++ tests/fish_realpath.in | 39 +++++++++++++++++++++++++++++++++++ tests/fish_realpath.out | 5 +++++ tests/fish_realpath.status | 1 + 7 files changed, 103 insertions(+) create mode 100644 doc_src/fish_realpath.txt create mode 100644 share/functions/realpath.fish create mode 100644 tests/fish_realpath.err create mode 100644 tests/fish_realpath.in create mode 100644 tests/fish_realpath.out create mode 100644 tests/fish_realpath.status diff --git a/doc_src/fish_realpath.txt b/doc_src/fish_realpath.txt new file mode 100644 index 000000000..a2ba0d0f9 --- /dev/null +++ b/doc_src/fish_realpath.txt @@ -0,0 +1,13 @@ +\section fish_realpath fish_realpath - Convert a path to an absolute path without symlinks + +\subsection fish_realpath-synopsis Synopsis +\fish{synopsis} +fish_realpath path +\endfish + +\subsection fish_realpath-description Description + +This is an implementation of the external realpath command that doesn't support any options. It's meant to be used only by scripts which need to be portable. In general scripts shouldn't invoke this directly. They should just use `realpath` which will fallback to this builtin if an external command cannot be found. + +If the path is invalid no translated path will be written to stdout and an error will be reported. +This implementation behaves like the GNU command being invoked with `--canonicalize-existing`. diff --git a/share/functions/realpath.fish b/share/functions/realpath.fish new file mode 100644 index 000000000..3d33ae0f3 --- /dev/null +++ b/share/functions/realpath.fish @@ -0,0 +1,13 @@ +# Provide a minimalist realpath implementation to help deal with platforms that may not provide it +# as a command. If a realpath command is available simply pass all arguments thru to it. If not +# fallback to alternative solutions. + +# The following is slightly subtle. The first time `realpath` is invoked this script will be read. +# If we see that there is an external command by that name we just return. That will cause fish to +# run the external command. On the other hand, if an external command isn't found we define a +# function that will provide fallback behavior. +if not type -q -P realpath + function realpath --description 'fallback realpath implementation' + builtin fish_realpath $argv[-1] + end +end diff --git a/src/builtin.cpp b/src/builtin.cpp index b03565436..70b379b1f 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -2988,6 +2988,33 @@ int builtin_false(parser_t &parser, io_streams_t &streams, wchar_t **argv) { return STATUS_BUILTIN_ERROR; } +/// An implementation of the external realpath command that doesn't support any options. It's meant +/// to be used only by scripts which need to be portable. In general scripts shouldn't invoke this +/// directly. They should just use `realpath` which will fallback to this builtin if an external +/// command cannot be found. This behaves like the external `realpath --canonicalize-existing`; +/// that is, it requires all path components, including the final, to exist. +int builtin_fish_realpath(parser_t &parser, io_streams_t &streams, wchar_t **argv) { + int argc = builtin_count_args(argv); + + if (argc != 2) { + streams.err.append_format(_(L"%ls: Expected one argument, got %d\n"), argv[0], argc - 1); + return STATUS_BUILTIN_ERROR; + } + + wchar_t *real_path = wrealpath(argv[1], NULL); + if (real_path) { + // Yay! We could resolve the path. + streams.out.append(real_path); + free((void *)real_path); + } else { + // The path isn't a simple filename and couldn't be resolved to an absolute path. + streams.err.append_format(_(L"%ls: Invalid path: %ls\n"), argv[0], argv[1]); + return STATUS_BUILTIN_ERROR; + } + streams.out.append(L"\n"); + return STATUS_BUILTIN_OK; +} + // END OF BUILTIN COMMANDS // Below are functions for handling the builtin commands. // THESE MUST BE SORTED BY NAME! Completion lookup uses binary search. @@ -3027,6 +3054,8 @@ static const builtin_data_t builtin_datas[] = { {L"exit", &builtin_exit, N_(L"Exit the shell")}, {L"false", &builtin_false, N_(L"Return an unsuccessful result")}, {L"fg", &builtin_fg, N_(L"Send job to foreground")}, + {L"fish_realpath", &builtin_fish_realpath, + N_(L"Convert path to absolute path without symlinks")}, {L"for", &builtin_generic, N_(L"Perform a set of commands multiple times")}, {L"function", &builtin_generic, N_(L"Define a new function")}, {L"functions", &builtin_functions, N_(L"List or remove functions")}, diff --git a/tests/fish_realpath.err b/tests/fish_realpath.err new file mode 100644 index 000000000..f12c36186 --- /dev/null +++ b/tests/fish_realpath.err @@ -0,0 +1,3 @@ +fish_realpath: Invalid path: /this/better/be/an/invalid/path +fish_realpath: Invalid path: nonexistent-file +fish_realpath: Invalid path: ../test/data/fish-symlink/nonexistent-file-relative-to-a-symlink diff --git a/tests/fish_realpath.in b/tests/fish_realpath.in new file mode 100644 index 000000000..f25309c99 --- /dev/null +++ b/tests/fish_realpath.in @@ -0,0 +1,39 @@ +# $XDG_DATA_HOME can itself be a relative path. So force it to an absolute +# path so we can remove it from any resolved paths below. This is needed +# because the contents of the fish_realpath.out file can't include any $PWD +# data since $PWD isn't under our control. +set -l data_home_realpath (fish_realpath $XDG_DATA_HOME) + +# A bogus absolute path is handled correctly and sets a failure status. +if not fish_realpath /this/better/be/an/invalid/path + echo first invalid path handled okay +end + +# A non-existent file relative to $PWD fails. +fish_realpath nonexistent-file + +# The simplest absolute path should undergo no transformation. +fish_realpath / + +# A single symlink to a directory is correctly resolved. +ln -s fish $XDG_DATA_HOME/fish-symlink +set -l real_path (fish_realpath $XDG_DATA_HOME/fish-symlink) +string replace "$data_home_realpath/" "" $real_path + +# A nonexistent file relative to a valid symlink to a directory fails. +# This depends on the symlink created by the previous test. +set -l real_path (fish_realpath $XDG_DATA_HOME/fish-symlink/nonexistent-file-relative-to-a-symlink) + +# A path with two symlinks, first to a directory, second to a file, is correctly resolved. +ln -s fish $XDG_DATA_HOME/fish-symlink2 +touch $XDG_DATA_HOME/fish/real_file +ln -s real_file $XDG_DATA_HOME/fish/symlink_file +set -l real_path (fish_realpath $XDG_DATA_HOME/fish-symlink/symlink_file) +string replace "$data_home_realpath/" "" $real_path + +# The $PWD should undergo no further transformations because it should already +# be a "realpath". +set -l real_path (fish_realpath $PWD) +string replace $PWD "pwd-resolved-to-itself" $real_path + +exit 0 diff --git a/tests/fish_realpath.out b/tests/fish_realpath.out new file mode 100644 index 000000000..d14bca83b --- /dev/null +++ b/tests/fish_realpath.out @@ -0,0 +1,5 @@ +first invalid path handled okay +/ +fish +fish/real_file +pwd-resolved-to-itself diff --git a/tests/fish_realpath.status b/tests/fish_realpath.status new file mode 100644 index 000000000..573541ac9 --- /dev/null +++ b/tests/fish_realpath.status @@ -0,0 +1 @@ +0 From cea65599e691a20037dd92660cdb31b0868d1824 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Wed, 13 Apr 2016 21:23:33 -0700 Subject: [PATCH 153/363] document how to config editors for this project --- CONTRIBUTING.md | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c3afbd8f3..0052ddf7b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -70,7 +70,33 @@ make style-all That command will refuse to restyle any files if you have uncommitted changes. -### Suppressing Reformatting of the Code +### Configuring Your Editor for Fish C++ Code + +#### ViM + +As of ViM 7.4 it does not recognize triple-slash comments as used by Doxygen and the OS X Xcode IDE to flag comments that explain the following C symbol. This means the `gq` key binding to reformat such comments doesn't behave as expected. You can fix that by adding the following to your vimrc: + +``` +autocmd Filetype c,cpp setlocal comments^=:/// +``` + +If you use ViM I recommend the [vim-clang-format plugin](https://github.com/rhysd/vim-clang-format) by [@rhysd](https://github.com/rhysd). + +You can also get ViM to provide reasonably correct behavior by installing + +http://www.vim.org/scripts/script.php?script_id=2636 + +#### Emacs + +If you use Emacs: TBD + +### Configuring Your Editor for Fish Scripts + +If you use ViM: TBD + +If you use Emacs: TBD + +### Suppressing Reformatting of C++ Code If you have a good reason for doing so you can tell `clang-format` to not reformat a block of code by enclosing it in comments like this: @@ -82,11 +108,11 @@ code to ignore ## Fish Script Style Guide -Fish scripts such as those in the *share/functions* and *tests* directories should be formatted using the `fish_indent` command. +1. Fish scripts such as those in the *share/functions* and *tests* directories should be formatted using the `fish_indent` command. -Function names should be all lowercase with undescores separating words. Private functions should begin with an underscore. The first word should be `fish` if the function is unique to fish. +1. Function names should be all lowercase with undescores separating words. Private functions should begin with an underscore. The first word should be `fish` if the function is unique to fish. -The first word of global variable names should generally be `fish` for public vars or `_fish` for private vars to minimize the possibility of name clashes with user defined vars. +1. The first word of global variable names should generally be `fish` for public vars or `_fish` for private vars to minimize the possibility of name clashes with user defined vars. ## C++ Style Guide From ea02da35d4efbc35530490e87c272189e07f030f Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Thu, 28 Apr 2016 18:26:46 -0700 Subject: [PATCH 154/363] restyle env module to match project style Reduces lint errors from 90 to 72 (-20%). Line count from 1719 to 1298 (-24%). Another step in resolving issue #2902. --- src/env.cpp | 1106 +++++++++++++++++---------------------------------- src/env.h | 303 ++++++-------- 2 files changed, 495 insertions(+), 914 deletions(-) diff --git a/src/env.cpp b/src/env.cpp index 22d4c5734..40af30bfb 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -1,353 +1,239 @@ -/** \file env.c - Functions for setting and getting environment variables. -*/ -#include -#include -#include -#include +// Functions for setting and getting environment variables. #include -#include +#include +#include #include #include -#include -#include -#include -#include +#include #include +#include +#include +#include +#include +#include #include +#include +#include +#include #include #include -#include -#include -#include "fallback.h" // IWYU pragma: keep -#include "wutil.h" // IWYU pragma: keep -#include "proc.h" #include "common.h" #include "env.h" -#include "sanity.h" -#include "expand.h" -#include "history.h" -#include "reader.h" #include "env_universal_common.h" -#include "input.h" #include "event.h" -#include "path.h" +#include "expand.h" +#include "fallback.h" // IWYU pragma: keep #include "fish_version.h" +#include "history.h" +#include "input.h" #include "input_common.h" +#include "path.h" +#include "proc.h" +#include "reader.h" +#include "sanity.h" +#include "wutil.h" // IWYU pragma: keep -/** Value denoting a null string */ +/// Value denoting a null string. #define ENV_NULL L"\x1d" -/** Some configuration path environment variables */ +/// Some configuration path environment variables. #define FISH_DATADIR_VAR L"__fish_datadir" #define FISH_SYSCONFDIR_VAR L"__fish_sysconfdir" #define FISH_HELPDIR_VAR L"__fish_help_dir" #define FISH_BIN_DIR L"__fish_bin_dir" -/** - At init, we read all the environment variables from this array. -*/ +/// At init, we read all the environment variables from this array. extern char **environ; -/** - This should be the same thing as \c environ, but it is possible only one of the two work... -*/ +/// This should be the same thing as \c environ, but it is possible only one of the two work... extern char **__environ; - bool g_log_forks = false; -bool g_use_posix_spawn = false; //will usually be set to true +bool g_use_posix_spawn = false; // will usually be set to true - -/** - Struct representing one level in the function variable stack -*/ -struct env_node_t -{ - /** - Variable table - */ +/// Struct representing one level in the function variable stack. +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. - */ + /// 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. bool new_scope; - /** - Does this node contain any variables which are exported to subshells - */ + /// Does this node contain any variables which are exported to subshells. bool exportv; - - /** - Pointer to next level - */ + /// Pointer to next level. struct env_node_t *next; + env_node_t() : new_scope(false), exportv(false), next(NULL) {} - env_node_t() : new_scope(false), exportv(false), next(NULL) { } - - /* Returns a pointer to the given entry if present, or NULL. */ + /// Returns a pointer to the given entry if present, or NULL. const var_entry_t *find_entry(const wcstring &key); - /* Returns the next scope to search in order, respecting the new_scope flag, or NULL if we're done. */ + /// Returns the next scope to search in order, respecting the new_scope flag, or NULL if we're + /// done. env_node_t *next_scope_to_search(); const env_node_t *next_scope_to_search() const; }; -class variable_entry_t -{ +class variable_entry_t { wcstring value; /**< Value of the variable */ }; static pthread_mutex_t env_lock = PTHREAD_MUTEX_INITIALIZER; -/** Top node on the function stack */ +/// Top node on the function stack. static env_node_t *top = NULL; -/** Bottom node on the function stack */ +/// Bottom node on the function stack. static env_node_t *global_env = NULL; -/** Universal variables global instance. Initialized in env_init. */ +/// Universal variables global instance. Initialized in env_init. static env_universal_t *s_universal_variables = NULL; -/* Getter for universal variables */ -static env_universal_t *uvars() { - return s_universal_variables; -} +/// Getter for universal variables. +static env_universal_t *uvars() { return s_universal_variables; } -/** - Table for global variables -*/ +/// Table for global variables. static var_table_t *global; -/* Helper class for storing constant strings, without needing to wrap them in a wcstring */ +// Helper class for storing constant strings, without needing to wrap them in a wcstring. -/* Comparer for const string set */ -struct const_string_set_comparer -{ - bool operator()(const wchar_t *a, const wchar_t *b) - { - return wcscmp(a, b) < 0; - } +// Comparer for const string set. +struct const_string_set_comparer { + bool operator()(const wchar_t *a, const wchar_t *b) { return wcscmp(a, b) < 0; } }; typedef std::set const_string_set_t; -/** Table of variables that may not be set using the set command. */ +/// Table of variables that may not be set using the set command. static const_string_set_t env_read_only; -static bool is_read_only(const wcstring &key) -{ +static bool is_read_only(const wcstring &key) { return env_read_only.find(key.c_str()) != env_read_only.end(); } -/** - Table of variables whose value is dynamically calculated, such as umask, status, etc -*/ +/// Table of variables whose value is dynamically calculated, such as umask, status, etc. static const_string_set_t env_electric; -static bool is_electric(const wcstring &key) -{ +static bool is_electric(const wcstring &key) { return env_electric.find(key.c_str()) != env_electric.end(); } -/** - Exported variable array used by execv -*/ +/// Exported variable array used by execv. static null_terminated_array_t export_array; -/** - Flag for checking if we need to regenerate the exported variable - array -*/ +/// Flag for checking if we need to regenerate the exported variable array. static bool has_changed_exported = true; -static void mark_changed_exported() -{ - has_changed_exported = true; -} +static void mark_changed_exported() { has_changed_exported = true; } -/** - List of all locale variable names -*/ -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 -}; +/// List of all locale variable names. +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}; - -const var_entry_t *env_node_t::find_entry(const wcstring &key) -{ +const var_entry_t *env_node_t::find_entry(const wcstring &key) { const var_entry_t *result = NULL; var_table_t::const_iterator where = env.find(key); - if (where != env.end()) - { + if (where != env.end()) { result = &where->second; } return result; } -env_node_t *env_node_t::next_scope_to_search() -{ +env_node_t *env_node_t::next_scope_to_search() { return this->new_scope ? global_env : this->next; } + +const env_node_t *env_node_t::next_scope_to_search() const { return this->new_scope ? global_env : this->next; } -const env_node_t *env_node_t::next_scope_to_search() const -{ - return this->new_scope ? global_env : this->next; -} - - -/** - Return the current umask value. -*/ -static mode_t get_umask() -{ +/// Return the current umask value. +static mode_t get_umask() { 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]) - { +/// Check if the specified variable is a locale variable. +static bool var_is_locale(const wcstring &key) { + for (size_t i = 0; locale_variable[i]; i++) { + if (key == locale_variable[i]) { return true; } } return false; } -/** - Properly sets all locale information -*/ -static void handle_locale() -{ +/// Properly sets all locale information. +static void handle_locale() { const env_var_t lc_all = env_get_string(L"LC_ALL"); 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 - } - ; + // 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()) - { + if (!lc_all.missing()) { wsetlocale(LC_ALL, lc_all.c_str()); - } - else - { + } else { const env_var_t lang = env_get_string(L"LANG"); - if (!lang.missing()) - { + if (!lang.missing()) { wsetlocale(LC_ALL, lang.c_str()); } - for (int i=2; locale_variable[i]; i++) - { + for (int i = 2; locale_variable[i]; i++) { const env_var_t val = env_get_string(locale_variable[i]); - if (!val.missing()) - { + 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. - */ - + 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++; - fish_dcgettext("fish", "Changing language to English", LC_MESSAGES); } } - -/** React to modifying the given variable */ -static void react_to_variable_change(const wcstring &key) -{ - if (var_is_locale(key)) - { +/// React to modifying the given variable. +static void react_to_variable_change(const wcstring &key) { + if (var_is_locale(key)) { handle_locale(); - } - else if (key == L"fish_term256" || key == L"fish_term24bit") - { + } else if (key == L"fish_term256" || key == L"fish_term24bit") { update_fish_color_support(); 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(); - } - else if (key == L"fish_escape_delay_ms") - { + } else if (key == L"fish_escape_delay_ms") { update_wait_on_escape_ms(); } } -/** - 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) -{ +/// 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) { const wchar_t *str = NULL; - switch (type) - { + switch (type) { case SET: - case SET_EXPORT: - { - str=L"SET"; + case SET_EXPORT: { + str = L"SET"; break; } - - case ERASE: - { - str=L"ERASE"; + case ERASE: { + str = L"ERASE"; break; } - - default: - break; + default: { break; } } - if (str) - { + if (str) { mark_changed_exported(); event_t ev = event_t::variable_event(name); @@ -357,81 +243,59 @@ static void universal_callback(fish_message_type_t type, const wchar_t *name, co event_fire(&ev); } - if (name) - react_to_variable_change(name); + if (name) react_to_variable_change(name); } -/** - Make sure the PATH variable contains something -*/ -static void setup_path() -{ +/// Make sure the PATH variable contains something. +static void setup_path() { const env_var_t path = env_get_string(L"PATH"); - if (path.missing_or_empty()) - { + if (path.missing_or_empty()) { const wchar_t *value = L"/usr/bin" ARRAY_SEP_STR L"/bin"; env_set(L"PATH", value, ENV_GLOBAL | ENV_EXPORT); } } -int env_set_pwd() -{ +int env_set_pwd() { wcstring res = wgetcwd(); - if (res.empty()) - { - debug(0, _(L"Could not determine current working directory. Is your locale set correctly?")); + if (res.empty()) { + debug(0, + _(L"Could not determine current working directory. Is your locale set correctly?")); return 0; } env_set(L"PWD", res.c_str(), ENV_EXPORT | ENV_GLOBAL); return 1; } -wcstring env_get_pwd_slash(void) -{ +wcstring env_get_pwd_slash(void) { env_var_t pwd = env_get_string(L"PWD"); - if (pwd.missing_or_empty()) - { + if (pwd.missing_or_empty()) { return L""; } - if (! string_suffixes_string(L"/", pwd)) - { + if (!string_suffixes_string(L"/", pwd)) { pwd.push_back(L'/'); } return pwd; } -/* Here is the whitelist of variables that we colon-delimit, both incoming from the environment and outgoing back to it. This is deliberately very short - we don't want to add language-specific values like CLASSPATH. */ -static bool variable_is_colon_delimited_array(const wcstring &str) -{ +// Here is the whitelist of variables that we colon-delimit, both incoming from the environment and +// outgoing back to it. This is deliberately very short - we don't want to add language-specific +// values like CLASSPATH. +static bool variable_is_colon_delimited_array(const wcstring &str) { return contains(str, L"PATH", L"MANPATH", L"CDPATH"); } -void env_init(const struct config_paths_t *paths /* or NULL */) -{ - /* - env_read_only variables can not be altered directly by the user - */ - - const wchar_t * const ro_keys[] = - { - L"status", - L"history", - L"version", - L"_", - L"LINES", - L"COLUMNS", - L"PWD", - //L"SHLVL", // will be inserted a bit lower down +void env_init(const struct config_paths_t *paths /* or NULL */) { + // env_read_only variables can not be altered directly by the user. + const wchar_t *const ro_keys[] = { + L"status", L"history", L"version", L"_", L"LINES", L"COLUMNS", L"PWD", + // L"SHLVL", // will be inserted a bit lower down 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]); } - /* - 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"); @@ -442,36 +306,28 @@ void env_init(const struct config_paths_t *paths /* or NULL */) 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. Walk backwards so that the first one out of any duplicates wins (#2784) */ + // Import environment variables. Walk backwards so that the first one out of any duplicates wins + // (#2784). wcstring key, val; - const char * const * envp = (environ ? environ : __environ); + const char *const *envp = (environ ? environ : __environ); size_t i = 0; - while (envp && envp[i]) - { + while (envp && envp[i]) { i++; } - while (i--) - { - const wcstring key_and_val = str2wcstring(envp[i]); //like foo=bar + while (i--) { + const wcstring key_and_val = str2wcstring(envp[i]); // like foo=bar size_t eql = key_and_val.find(L'='); - if (eql == wcstring::npos) - { - // no equals found + if (eql == wcstring::npos) { + // No equals found. if (is_read_only(key_and_val) || is_electric(key_and_val)) continue; env_set(key_and_val, L"", ENV_EXPORT | ENV_GLOBAL); - } - else - { + } else { key.assign(key_and_val, 0, eql); if (is_read_only(key) || is_electric(key)) continue; val.assign(key_and_val, eql + 1, wcstring::npos); - if (variable_is_colon_delimited_array(key)) - { + if (variable_is_colon_delimited_array(key)) { std::replace(val.begin(), val.end(), L':', ARRAY_SEP); } @@ -479,111 +335,89 @@ void env_init(const struct config_paths_t *paths /* or NULL */) } } - /* Set the given paths in the environment, if we have any */ - if (paths != NULL) - { + // Set the given paths in the environment, if we have any. + if (paths != NULL) { env_set(FISH_DATADIR_VAR, paths->data.c_str(), ENV_GLOBAL); env_set(FISH_SYSCONFDIR_VAR, paths->sysconf.c_str(), ENV_GLOBAL); env_set(FISH_HELPDIR_VAR, paths->doc.c_str(), ENV_GLOBAL); env_set(FISH_BIN_DIR, paths->bin.c_str(), ENV_GLOBAL); } - /* - Set up the PATH variable - */ + // Set up the PATH variable. setup_path(); - /* - Set up the USER variable - */ - if (env_get_string(L"USER").missing_or_empty()) - { + // Set up the USER variable. + if (env_get_string(L"USER").missing_or_empty()) { const struct passwd *pw = getpwuid(getuid()); - if (pw && pw->pw_name) - { + if (pw && pw->pw_name) { const wcstring uname = str2wcstring(pw->pw_name); env_set(L"USER", uname.c_str(), ENV_GLOBAL | ENV_EXPORT); } } - /* - Set up the version variables - */ + // Set up the version variables. wcstring version = str2wcstring(get_fish_version()); env_set(L"version", version.c_str(), ENV_GLOBAL); env_set(L"FISH_VERSION", version.c_str(), ENV_GLOBAL); - /* - Set up SHLVL variable - */ + // 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()) { wchar_t *end; long shlvl_i = wcstol(shlvl_str.c_str(), &end, 10); - while (iswspace(*end)) ++end; /* skip trailing whitespace */ - if (shlvl_i >= 0 && *end == '\0') - { + while (iswspace(*end)) ++end; // skip trailing whitespace + if (shlvl_i >= 0 && *end == '\0') { nshlvl_str = to_string(shlvl_i + 1); } } env_set(L"SHLVL", nshlvl_str.c_str(), ENV_GLOBAL | ENV_EXPORT); env_read_only.insert(L"SHLVL"); - /* Set up the HOME variable */ - if (env_get_string(L"HOME").missing_or_empty()) - { + // Set up the HOME variable. + if (env_get_string(L"HOME").missing_or_empty()) { const env_var_t unam = env_get_string(L"USER"); char *unam_narrow = wcs2str(unam.c_str()); struct passwd *pw = getpwnam(unam_narrow); - if (pw->pw_dir != NULL) - { + if (pw->pw_dir != NULL) { const wcstring dir = str2wcstring(pw->pw_dir); env_set(L"HOME", dir.c_str(), ENV_GLOBAL | ENV_EXPORT); } free(unam_narrow); } - /* Set PWD */ + // Set PWD. env_set_pwd(); - /* Set up universal variables. The empty string means to use the deafult path. */ + // Set up universal variables. The empty string means to use the deafult path. assert(s_universal_variables == NULL); s_universal_variables = new env_universal_t(L""); s_universal_variables->load(); - /* Set g_log_forks */ + // Set g_log_forks. env_var_t log_forks = env_get_string(L"fish_log_forks"); - g_log_forks = ! log_forks.missing_or_empty() && from_string(log_forks); + g_log_forks = !log_forks.missing_or_empty() && from_string(log_forks); - /* Set g_use_posix_spawn. Default to true. */ + // Set g_use_posix_spawn. Default to true. env_var_t use_posix_spawn = env_get_string(L"fish_use_posix_spawn"); - g_use_posix_spawn = (use_posix_spawn.missing_or_empty() ? true : from_string(use_posix_spawn)); + g_use_posix_spawn = + (use_posix_spawn.missing_or_empty() ? true : from_string(use_posix_spawn)); - /* Set fish_bind_mode to "default" */ + // Set fish_bind_mode to "default". env_set(FISH_BIND_MODE_VAR, DEFAULT_BIND_MODE, ENV_GLOBAL); - /* - Now that the global scope is fully initialized, add a toplevel local - scope. This same local scope will persist throughout the lifetime of the - fish process, and it will ensure that `set -l` commands run at the - command-line don't affect the global scope. - */ + // Now that the global scope is fully initialized, add a toplevel local scope. This same local + // scope will persist throughout the lifetime of the fish process, and it will ensure that `set + // -l` commands run at the command-line don't affect the global scope. env_push(false); } -/** - 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) -{ +/// 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) { env_node_t *env = top; - while (env != NULL) - { - if (env->find_entry(key) != NULL) - { + while (env != NULL) { + if (env->find_entry(key) != NULL) { break; } @@ -592,53 +426,42 @@ static env_node_t *env_get_node(const wcstring &key) return env; } -int env_set(const wcstring &key, const wchar_t *val, env_mode_flags_t var_mode) -{ +int env_set(const wcstring &key, const wchar_t *val, env_mode_flags_t var_mode) { ASSERT_IS_MAIN_THREAD(); bool has_changed_old = has_changed_exported; - int done=0; + int done = 0; - if (val && contains(key, L"PWD", L"HOME")) - { - /* Canoncalize our path; if it changes, recurse and try again. */ + if (val && contains(key, L"PWD", L"HOME")) { + // Canonicalize our path; if it changes, recurse and try again. wcstring val_canonical = val; path_make_canonical(val_canonical); - if (val != val_canonical) - { + if (val != val_canonical) { return env_set(key, val_canonical.c_str(), var_mode); } } - if ((var_mode & (ENV_LOCAL | ENV_UNIVERSAL)) && (is_read_only(key) || is_electric(key))) - { + if ((var_mode & (ENV_LOCAL | ENV_UNIVERSAL)) && (is_read_only(key) || is_electric(key))) { return ENV_SCOPE; } - if ((var_mode & ENV_EXPORT) && is_electric(key)) - { + if ((var_mode & ENV_EXPORT) && is_electric(key)) { return ENV_SCOPE; } - - if ((var_mode & ENV_USER) && is_read_only(key)) - { + if ((var_mode & ENV_USER) && is_read_only(key)) { return ENV_PERM; } - if (key == L"umask") - { + if (key == L"umask") { wchar_t *end; - /* - Set the new umask - */ - if (val && wcslen(val)) - { - errno=0; + // Set the new umask. + if (val && wcslen(val)) { + errno = 0; long mask = wcstol(val, &end, 8); - if (!errno && (!*end) && (mask <= 0777) && (mask >= 0)) - { + if (!errno && (!*end) && (mask <= 0777) && (mask >= 0)) { umask(mask); - /* Do not actually create a umask variable, on env_get, it will be calculated dynamically */ + // Do not actually create a umask variable, on env_get, it will be calculated + // dynamically. return 0; } } @@ -646,102 +469,71 @@ int env_set(const wcstring &key, const wchar_t *val, env_mode_flags_t var_mode) return ENV_INVALID; } - /* - Zero element arrays are internaly not coded as null but as this - placeholder string - */ - if (!val) - { + // Zero element arrays are internaly not coded as null but as this placeholder string. + if (!val) { val = ENV_NULL; } - if (var_mode & ENV_UNIVERSAL) - { + if (var_mode & ENV_UNIVERSAL) { const bool old_export = uvars() && uvars()->get_export(key); bool new_export; - if (var_mode & ENV_EXPORT) - { - // export + if (var_mode & ENV_EXPORT) { + // Export the var. new_export = true; - } - else if (var_mode & ENV_UNEXPORT) - { - // unexport + } else if (var_mode & ENV_UNEXPORT) { + // Unexport the var. new_export = false; - } - else - { - // not changing the export + } else { + // Not changing the export status of the var. new_export = old_export; } - if (uvars()) - { + if (uvars()) { uvars()->set(key, val, new_export); env_universal_barrier(); - if (old_export || new_export) - { + if (old_export || new_export) { mark_changed_exported(); } } - } - else - { - // Determine the node + } else { + // Determine the node. bool has_changed_new = false; env_node_t *preexisting_node = env_get_node(key); bool preexisting_entry_exportv = false; - if (preexisting_node != NULL) - { + if (preexisting_node != NULL) { var_table_t::const_iterator result = preexisting_node->env.find(key); assert(result != preexisting_node->env.end()); const var_entry_t &entry = result->second; - if (entry.exportv) - { + if (entry.exportv) { preexisting_entry_exportv = true; has_changed_new = true; } } env_node_t *node = NULL; - if (var_mode & ENV_GLOBAL) - { + if (var_mode & ENV_GLOBAL) { node = global_env; - } - else if (var_mode & ENV_LOCAL) - { + } else if (var_mode & ENV_LOCAL) { node = top; - } - else if (preexisting_node != NULL) - { + } else if (preexisting_node != NULL) { node = preexisting_node; - if ((var_mode & (ENV_EXPORT | ENV_UNEXPORT)) == 0) - { + if ((var_mode & (ENV_EXPORT | ENV_UNEXPORT)) == 0) { // use existing entry's exportv var_mode = preexisting_entry_exportv ? ENV_EXPORT : 0; } - } - else - { - if (! get_proc_had_barrier()) - { + } else { + if (!get_proc_had_barrier()) { set_proc_had_barrier(true); env_universal_barrier(); } - if (uvars() && ! uvars()->get(key).missing()) - { + if (uvars() && !uvars()->get(key).missing()) { bool exportv; - if (var_mode & ENV_EXPORT) - { + if (var_mode & ENV_EXPORT) { exportv = true; - } - else if (var_mode & ENV_UNEXPORT) - { + } else if (var_mode & ENV_UNEXPORT) { exportv = false; - } - else - { + } else { exportv = uvars()->get_export(key); } @@ -750,48 +542,35 @@ int env_set(const wcstring &key, const wchar_t *val, env_mode_flags_t var_mode) 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. - */ + } 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) - { + while (node->next && !node->new_scope) { node = node->next; } } } - if (!done) - { - // Set the entry in the node - // Note that operator[] accesses the existing entry, or creates a new one + if (!done) { + // Set the entry in the node. Note that operator[] accesses the existing entry, or + // creates a new one. var_entry_t &entry = node->env[key]; - if (entry.exportv) - { - // this variable already existed, and was exported + if (entry.exportv) { + // This variable already existed, and was exported. has_changed_new = true; } entry.val = val; - if (var_mode & ENV_EXPORT) - { - // the new variable is exported + if (var_mode & ENV_EXPORT) { + // The new variable is exported. entry.exportv = true; node->exportv = true; has_changed_new = true; - } - else - { + } else { entry.exportv = false; } - if (has_changed_old || has_changed_new) - mark_changed_exported(); + if (has_changed_old || has_changed_new) mark_changed_exported(); } } @@ -801,79 +580,59 @@ int env_set(const wcstring &key, const wchar_t *val, env_mode_flags_t var_mode) ev.arguments.push_back(L"SET"); ev.arguments.push_back(key); - // debug( 1, L"env_set: fire events on variable %ls", key ); + // debug( 1, L"env_set: fire events on variable %ls", key ); event_fire(&ev); - // debug( 1, L"env_set: return from event firing" ); + // debug( 1, L"env_set: return from event firing" ); react_to_variable_change(key); - return 0; } - -/** - Attempt to remove/free the specified key/value pair from the - specified map. - - \return zero if the variable was not found, non-zero otherwise -*/ -static bool try_remove(env_node_t *n, const wchar_t *key, int var_mode) -{ - if (n == NULL) - { +/// Attempt to remove/free the specified key/value pair from the specified map. +/// +/// \return zero if the variable was not found, non-zero otherwise +static bool try_remove(env_node_t *n, const wchar_t *key, int var_mode) { + if (n == NULL) { return false; } var_table_t::iterator result = n->env.find(key); - if (result != n->env.end()) - { - if (result->second.exportv) - { + if (result != n->env.end()) { + if (result->second.exportv) { mark_changed_exported(); } n->env.erase(result); return true; } - if (var_mode & ENV_LOCAL) - { + if (var_mode & ENV_LOCAL) { return false; } - if (n->new_scope) - { + if (n->new_scope) { return try_remove(global_env, key, var_mode); - } - else - { + } 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; - if ((var_mode & ENV_USER) && is_read_only(key)) - { + 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_UNIVERSAL)) { + if (var_mode & ENV_GLOBAL) { first_node = global_env; } - if (try_remove(first_node, key.c_str(), var_mode)) - { + if (try_remove(first_node, key.c_str(), var_mode)) { event_t ev = event_t::variable_event(key); ev.arguments.push_back(L"VARIABLE"); ev.arguments.push_back(L"ERASE"); @@ -884,14 +643,10 @@ int env_remove(const wcstring &key, int var_mode) } } - if (!erased && - !(var_mode & ENV_GLOBAL) && - !(var_mode & ENV_LOCAL)) - { + if (!erased && !(var_mode & ENV_GLOBAL) && !(var_mode & ENV_LOCAL)) { bool is_exported = uvars()->get_export(key); erased = uvars() && uvars()->remove(key); - if (erased) - { + if (erased) { env_universal_barrier(); event_t ev = event_t::variable_event(key); ev.arguments.push_back(L"VARIABLE"); @@ -899,9 +654,8 @@ int env_remove(const wcstring &key, int var_mode) ev.arguments.push_back(key); event_fire(&ev); } - - if (is_exported) - mark_changed_exported(); + + if (is_exported) mark_changed_exported(); } react_to_variable_change(key); @@ -909,14 +663,12 @@ int env_remove(const wcstring &key, int var_mode) return !erased; } -const wchar_t *env_var_t::c_str(void) const -{ - assert(! is_missing); +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_mode_flags_t mode) -{ +env_var_t env_get_string(const wcstring &key, env_mode_flags_t mode) { const bool has_scope = mode & (ENV_LOCAL | ENV_GLOBAL | ENV_UNIVERSAL); const bool search_local = !has_scope || (mode & ENV_LOCAL); const bool search_global = !has_scope || (mode & ENV_GLOBAL); @@ -925,41 +677,31 @@ env_var_t env_get_string(const wcstring &key, env_mode_flags_t mode) const bool search_exported = (mode & ENV_EXPORT) || !(mode & ENV_UNEXPORT); const bool search_unexported = (mode & ENV_UNEXPORT) || !(mode & ENV_EXPORT); - /* Make the assumption that electric keys can't be shadowed elsewhere, since we currently block that in env_set() */ - if (is_electric(key)) - { + // Make the assumption that electric keys can't be shadowed elsewhere, since we currently block + // that in env_set(). + if (is_electric(key)) { if (!search_global) return env_var_t::missing_var(); - /* 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) */ - if (key == L"history" && is_main_thread()) - { + // 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). + if (key == L"history" && is_main_thread()) { 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); + if (history) history->get_string_representation(&result, ARRAY_SEP_STR); return result; - } - else if (key == L"COLUMNS") - { + } 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_height()); - } - else if (key == L"status") - { + } else if (key == L"status") { return to_string(proc_get_last_status()); - } - else if (key == L"umask") - { + } else if (key == L"umask") { return format_string(L"0%0.3o", get_umask()); } - // we should never get here unless the electric var list is out of sync + // We should never get here unless the electric var list is out of sync. } if (search_local || search_global) { @@ -968,28 +710,20 @@ env_var_t env_get_string(const wcstring &key, env_mode_flags_t mode) env_node_t *env = search_local ? top : global_env; - while (env != NULL) - { + while (env != NULL) { const var_entry_t *entry = env->find_entry(key); - if (entry != NULL && (entry->exportv ? search_exported : search_unexported)) - { - if (entry->val == ENV_NULL) - { + if (entry != NULL && (entry->exportv ? search_exported : search_unexported)) { + if (entry->val == ENV_NULL) { return env_var_t::missing_var(); - } - else - { + } else { return entry->val; } } - if (has_scope) - { + if (has_scope) { if (!search_global || env == global_env) break; env = global_env; - } - else - { + } else { env = env->next_scope_to_search(); } } @@ -997,19 +731,17 @@ env_var_t env_get_string(const wcstring &key, env_mode_flags_t mode) if (!search_universal) return env_var_t::missing_var(); - /* 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_thread() && ! get_proc_had_barrier()) - { + // Another 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_thread() && !get_proc_had_barrier()) { set_proc_had_barrier(true); env_universal_barrier(); } - if (uvars()) - { + if (uvars()) { env_var_t env_var = uvars()->get(key); - if (env_var == ENV_NULL || !(uvars()->get_export(key) ? search_exported : search_unexported)) - { + if (env_var == ENV_NULL || + !(uvars()->get_export(key) ? search_exported : search_unexported)) { env_var = env_var_t::missing_var(); } return env_var; @@ -1017,8 +749,7 @@ env_var_t env_get_string(const wcstring &key, env_mode_flags_t mode) return env_var_t::missing_var(); } -bool env_exist(const wchar_t *key, env_mode_flags_t mode) -{ +bool env_exist(const wchar_t *key, env_mode_flags_t mode) { CHECK(key, false); const bool has_scope = mode & (ENV_LOCAL | ENV_GLOBAL | ENV_UNIVERSAL); @@ -1029,32 +760,24 @@ bool env_exist(const wchar_t *key, env_mode_flags_t mode) const bool test_exported = (mode & ENV_EXPORT) || !(mode & ENV_UNEXPORT); const bool test_unexported = (mode & ENV_UNEXPORT) || !(mode & ENV_EXPORT); - if (is_electric(key)) - { - /* - Electric variables all exist, and they are all global. A local or - universal version can not exist. They are also never exported. - */ - if (test_global && test_unexported) - { + if (is_electric(key)) { + // Electric variables all exist, and they are all global. A local or universal version can + // not exist. They are also never exported. + if (test_global && test_unexported) { return true; } return false; } - if (test_local || test_global) - { + if (test_local || test_global) { const env_node_t *env = test_local ? top : global_env; - while (env != NULL) - { - if (env == global_env && ! test_global) - { + while (env != NULL) { + if (env == global_env && !test_global) { break; } - + var_table_t::const_iterator result = env->env.find(key); - if (result != env->env.end()) - { + if (result != env->env.end()) { const var_entry_t &res = result->second; return res.exportv ? test_exported : test_unexported; } @@ -1062,16 +785,13 @@ bool env_exist(const wchar_t *key, env_mode_flags_t mode) } } - if (test_universal) - { - if (! get_proc_had_barrier()) - { + if (test_universal) { + if (!get_proc_had_barrier()) { set_proc_had_barrier(true); env_universal_barrier(); } - if (uvars() && ! uvars()->get(key).missing()) - { + if (uvars() && !uvars()->get(key).missing()) { return uvars()->get_export(key) ? test_exported : test_unexported; } } @@ -1079,73 +799,54 @@ bool env_exist(const wchar_t *key, env_mode_flags_t mode) 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) -{ +/// 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) { + 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); } -void env_push(bool new_scope) -{ +void env_push(bool new_scope) { env_node_t *node = new env_node_t; node->next = top; - node->new_scope=new_scope; + node->new_scope = new_scope; - if (new_scope) - { - if (local_scope_exports(top)) - mark_changed_exported(); + if (new_scope) { + if (local_scope_exports(top)) mark_changed_exported(); } top = node; - } - -void env_pop() -{ - if (&top->env != global) - { +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++) - { - var_table_t::iterator result = killme->env.find(locale_variable[i]); - if (result != killme->env.end()) - { + 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(); + 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) - { + for (iter = killme->env.begin(); iter != killme->env.end(); ++iter) { const var_entry_t &entry = iter->second; - if (entry.exportv) - { + if (entry.exportv) { mark_changed_exported(); break; } @@ -1153,40 +854,29 @@ void env_pop() delete killme; - if (locale_changed) - handle_locale(); + if (locale_changed) handle_locale(); - } - else - { - debug(0, - _(L"Tried to pop empty environment stack.")); + } else { + debug(0, _(L"Tried to pop empty environment stack.")); sanity_lose(); } } -/** - Function used with to insert keys of one table into a set::set -*/ -static void add_key_to_string_set(const var_table_t &envs, std::set *str_set, bool show_exported, bool show_unexported) -{ +/// Function used with to insert keys of one table into a set::set. +static void add_key_to_string_set(const var_table_t &envs, std::set *str_set, + bool show_exported, bool show_unexported) { var_table_t::const_iterator iter; - for (iter = envs.begin(); iter != envs.end(); ++iter) - { + for (iter = envs.begin(); iter != envs.end(); ++iter) { const var_entry_t &e = iter->second; - if ((e.exportv && show_exported) || - (!e.exportv && show_unexported)) - { - /* Insert this key */ + if ((e.exportv && show_exported) || (!e.exportv && show_unexported)) { + // Insert this key. str_set->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; @@ -1195,42 +885,34 @@ wcstring_list_t env_get_names(int flags) int show_global = flags & ENV_GLOBAL; int show_universal = flags & ENV_UNIVERSAL; - env_node_t *n=top; + env_node_t *n = top; const bool show_exported = (flags & ENV_EXPORT) || !(flags & ENV_UNEXPORT); const bool 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 && !show_global && !show_universal) { + show_local = show_universal = show_global = 1; } - if (show_local) - { - while (n) - { - if (n == global_env) - break; + if (show_local) { + while (n) { + if (n == global_env) break; add_key_to_string_set(n->env, &names, show_exported, show_unexported); if (n->new_scope) break; else n = n->next; - } } - if (show_global) - { + if (show_global) { add_key_to_string_set(global_env->env, &names, show_exported, show_unexported); - if (show_unexported) - { + if (show_unexported) { result.insert(result.end(), env_electric.begin(), env_electric.end()); } } - if (show_universal && uvars()) - { + if (show_universal && uvars()) { const wcstring_list_t uni_list = uvars()->get_names(show_exported, show_unexported); names.insert(uni_list.begin(), uni_list.end()); } @@ -1239,14 +921,9 @@ wcstring_list_t env_get_names(int flags) return result; } -/** - Get list of all exported variables -*/ - -static void get_exported(const env_node_t *n, std::map *h) -{ - if (!n) - return; +/// Get list of all exported variables. +static void get_exported(const env_node_t *n, std::map *h) { + if (!n) return; if (n->new_scope) get_exported(global_env, h); @@ -1254,86 +931,73 @@ static void get_exported(const env_node_t *n, std::map *h) get_exported(n->next, h); var_table_t::const_iterator iter; - for (iter = n->env.begin(); iter != n->env.end(); ++iter) - { + for (iter = n->env.begin(); iter != n->env.end(); ++iter) { const wcstring &key = iter->first; const var_entry_t &val_entry = iter->second; - if (val_entry.exportv && val_entry.val != ENV_NULL) - { - // Export the variable - // Don't use std::map::insert here, since we need to overwrite existing - // values from previous scopes + if (val_entry.exportv && val_entry.val != ENV_NULL) { + // Export the variable. Don't use std::map::insert here, since we need to overwrite + // existing values from previous scopes. (*h)[key] = val_entry.val; - } - else - { - // We need to erase from the map if we are not exporting, - // since a lower scope may have exported. See #2132 + } else { + // We need to erase from the map if we are not exporting, since a lower scope may have + // exported. See #2132. h->erase(key); } } } -/* Given a map from key to value, add values to out of the form key=value */ -static void export_func(const std::map &envs, std::vector &out) -{ +// Given a map from key to value, add values to out of the form key=value. +static void export_func(const std::map &envs, std::vector &out) { out.reserve(out.size() + envs.size()); std::map::const_iterator iter; - for (iter = envs.begin(); iter != envs.end(); ++iter) - { + for (iter = envs.begin(); iter != envs.end(); ++iter) { const wcstring &key = iter->first; const std::string &ks = wcs2string(key); std::string vs = wcs2string(iter->second); - /* Arrays in the value are ASCII record separator (0x1e) delimited. But some variables should have colons. Add those. */ - if (variable_is_colon_delimited_array(key)) - { - /* Replace ARRAY_SEP with colon */ + // Arrays in the value are ASCII record separator (0x1e) delimited. But some variables + // should have colons. Add those. + if (variable_is_colon_delimited_array(key)) { + // Replace ARRAY_SEP with colon. std::replace(vs.begin(), vs.end(), (char)ARRAY_SEP, ':'); } - /* Put a string on the vector */ + // Put a string on the vector. out.push_back(std::string()); std::string &str = out.back(); str.reserve(ks.size() + 1 + vs.size()); - /* Append our environment variable data to it */ + // Append our environment variable data to it. str.append(ks); str.append("="); str.append(vs); } } -static void update_export_array_if_necessary(bool recalc) -{ +static void update_export_array_if_necessary(bool recalc) { ASSERT_IS_MAIN_THREAD(); - if (recalc && ! get_proc_had_barrier()) - { + if (recalc && !get_proc_had_barrier()) { set_proc_had_barrier(true); env_universal_barrier(); } - if (has_changed_exported) - { + if (has_changed_exported) { std::map vals; debug(4, L"env_export_arr() recalc"); get_exported(top, &vals); - if (uvars()) - { + if (uvars()) { const wcstring_list_t uni = uvars()->get_names(true, false); - for (size_t i=0; iget(key); - if (! val.missing() && val != ENV_NULL) - { + if (!val.missing() && val != ENV_NULL) { // Note that std::map::insert does NOT overwrite a value already in the map, - // which we depend on here + // which we depend on here. vals.insert(std::pair(key, val)); } } @@ -1342,107 +1006,83 @@ static void update_export_array_if_necessary(bool recalc) std::vector local_export_buffer; export_func(vals, local_export_buffer); export_array.set(local_export_buffer); - has_changed_exported=false; + has_changed_exported = false; } - } -const char * const *env_export_arr(bool recalc) -{ +const char *const *env_export_arr(bool recalc) { ASSERT_IS_MAIN_THREAD(); update_export_array_if_necessary(recalc); return export_array.get(); } -void env_set_argv(const wchar_t * const * argv) -{ - if (*argv) - { - const wchar_t * const *arg; +void env_set_argv(const wchar_t *const *argv) { + if (*argv) { + const wchar_t *const *arg; wcstring sb; - for (arg=argv; *arg; arg++) - { - if (arg != argv) - { + 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 - { + } else { env_set(L"argv", 0, ENV_LOCAL); } } -env_vars_snapshot_t::env_vars_snapshot_t(const wchar_t * const *keys) -{ +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; } } } - -void env_universal_barrier() -{ +void env_universal_barrier() { ASSERT_IS_MAIN_THREAD(); - if (uvars()) - { + if (uvars()) { callback_data_list_t changes; bool changed = uvars()->sync(&changes); - if (changed) - { + if (changed) { universal_notifier_t::default_notifier().post_notification(); } - - /* Post callbacks */ - for (size_t i=0; i < changes.size(); i++) - { + + // Post callbacks. + for (size_t i = 0; i < changes.size(); i++) { const callback_data_t &data = changes.at(i); universal_callback(data.type, data.key.c_str(), data.val.c_str()); } } } -env_vars_snapshot_t::env_vars_snapshot_t() { } +env_vars_snapshot_t::env_vars_snapshot_t() {} -/* The "current" variables are not a snapshot at all, but instead trampoline to env_get_string, etc. We identify the current snapshot based on pointer values. */ +// The "current" variables are not a snapshot at all, but instead trampoline to env_get_string, etc. +// We identify the current snapshot based on pointer values. static const env_vars_snapshot_t sCurrentSnapshot; -const env_vars_snapshot_t &env_vars_snapshot_t::current() -{ - return sCurrentSnapshot; -} +const env_vars_snapshot_t &env_vars_snapshot_t::current() { return sCurrentSnapshot; } -bool env_vars_snapshot_t::is_current() const -{ - return this == &sCurrentSnapshot; -} +bool env_vars_snapshot_t::is_current() const { return this == &sCurrentSnapshot; } -env_var_t env_vars_snapshot_t::get(const wcstring &key) const -{ - /* If we represent the current state, bounce to env_get_string */ - if (this->is_current()) - { +env_var_t env_vars_snapshot_t::get(const wcstring &key) const { + // If we represent the current state, bounce to env_get_string. + if (this->is_current()) { 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)); } } -const wchar_t * const env_vars_snapshot_t::highlighting_keys[] = {L"PATH", L"CDPATH", L"fish_function_path", NULL}; +const wchar_t *const env_vars_snapshot_t::highlighting_keys[] = {L"PATH", L"CDPATH", + L"fish_function_path", NULL}; -const wchar_t * const env_vars_snapshot_t::completing_keys[] = {L"PATH", L"CDPATH", NULL}; +const wchar_t *const env_vars_snapshot_t::completing_keys[] = {L"PATH", L"CDPATH", NULL}; diff --git a/src/env.h b/src/env.h index 4b14d548c..682cf88d3 100644 --- a/src/env.h +++ b/src/env.h @@ -1,250 +1,196 @@ -/** \file env.h - Prototypes for functions for setting and getting environment variables. -*/ +// Prototypes for functions for setting and getting environment variables. #ifndef FISH_ENV_H #define FISH_ENV_H -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include #include "common.h" -/* Flags that may be passed as the 'mode' in env_set / env_get_string */ -enum -{ - /* Default mode */ +// Flags that may be passed as the 'mode' in env_set / env_get_string. +enum { + /// Default mode. ENV_DEFAULT = 0, - - /** Flag for local (to the current block) variable */ + + /// Flag for local (to the current block) variable. ENV_LOCAL = 1, - - /** Flag for exported (to commands) variable */ + + /// Flag for exported (to commands) variable. ENV_EXPORT = 2, - - /** Flag for unexported variable */ + + /// Flag for unexported variable. ENV_UNEXPORT = 16, - - /** Flag for global variable */ + + /// Flag for global variable. ENV_GLOBAL = 4, - - /** Flag for variable update request from the user. All variable - changes that are made directly by the user, such as those from the - 'set' builtin must have this flag set. */ + + /// Flag for variable update request from the user. All variable changes that are made directly + /// by the user, such as those from the 'set' builtin must have this flag set. ENV_USER = 8, - - /** Flag for universal variable */ + + /// Flag for universal variable. ENV_UNIVERSAL = 32 }; typedef uint32_t env_mode_flags_t; -/** - Error code for trying to alter read-only variable -*/ -enum -{ - ENV_PERM = 1, - ENV_SCOPE, - ENV_INVALID -} -; +/// Error code for trying to alter read-only variable. +enum { ENV_PERM = 1, ENV_SCOPE, ENV_INVALID }; -/* A struct of configuration directories, determined in main() that fish will optionally pass to env_init. - */ -struct config_paths_t -{ - wcstring data; // e.g. /usr/local/share - wcstring sysconf; // e.g. /usr/local/etc - wcstring doc; // e.g. /usr/local/share/doc/fish - wcstring bin; // e.g. /usr/local/bin +/// A struct of configuration directories, determined in main() that fish will optionally pass to +/// env_init. +struct config_paths_t { + wcstring data; // e.g. /usr/local/share + wcstring sysconf; // e.g. /usr/local/etc + wcstring doc; // e.g. /usr/local/share/doc/fish + wcstring bin; // e.g. /usr/local/bin }; -/** - Initialize environment variable data -*/ +/// Initialize environment variable data. void env_init(const struct config_paths_t *paths = NULL); -/** - Set the value of the environment variable whose name matches key to val. - - Memory policy: All keys and values are copied, the parameters can and should be freed by the caller afterwards - - \param key The key - \param val The value - \param mode The type of the variable. Can be any combination of ENV_GLOBAL, ENV_LOCAL, ENV_EXPORT and ENV_USER. If mode is zero, the current variable space is searched and the current mode is used. If no current variable with the same name is found, ENV_LOCAL is assumed. - - \returns 0 on suicess or an error code on failiure. - - The current error codes are: - - * ENV_PERM, can only be returned when setting as a user, e.g. ENV_USER is set. This means that the user tried to change a read-only variable. - * ENV_SCOPE, the variable cannot be set in the given scope. This applies to readonly/electric variables set from the local or universal scopes, or set as exported. - * ENV_INVALID, the variable value was invalid. This applies only to special variables. -*/ - +/// Set the value of the environment variable whose name matches key to val. +/// +/// Memory policy: All keys and values are copied, the parameters can and should be freed by the +/// caller afterwards +/// +/// \param key The key +/// \param val The value +/// \param mode The type of the variable. Can be any combination of ENV_GLOBAL, ENV_LOCAL, +/// ENV_EXPORT and ENV_USER. If mode is zero, the current variable space is searched and the current +/// mode is used. If no current variable with the same name is found, ENV_LOCAL is assumed. +/// +/// \returns 0 on success or an error code on failiure. +/// +/// The current error codes are: +/// +/// * ENV_PERM, can only be returned when setting as a user, e.g. ENV_USER is set. This means that +/// the user tried to change a read-only variable. +/// * ENV_SCOPE, the variable cannot be set in the given scope. This applies to readonly/electric +/// variables set from the local or universal scopes, or set as exported. +/// * ENV_INVALID, the variable value was invalid. This applies only to special variables. int env_set(const wcstring &key, const wchar_t *val, env_mode_flags_t mode); - -/** - Return the value of the variable with the specified name. Returns 0 - if the key does not exist. The returned string should not be - modified or freed. The returned string is only guaranteed to be - valid until the next call to env_get(), env_set(), env_push() or - env_pop() takes place. -*/ -//const wchar_t *env_get( const wchar_t *key ); - -class env_var_t : public wcstring -{ -private: +class env_var_t : public wcstring { + private: bool is_missing; -public: - static env_var_t missing_var() - { + + public: + static env_var_t missing_var() { env_var_t result((wcstring())); result.is_missing = true; return result; } - env_var_t(const env_var_t &x) : wcstring(x), is_missing(x.is_missing) { } - 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) { } + env_var_t(const env_var_t &x) : wcstring(x), is_missing(x.is_missing) {} + 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(void) const { return is_missing; } - bool missing_or_empty(void) const - { - return missing() || empty(); - } + 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 - { - return is_missing == s.is_missing && static_cast(*this) == static_cast(s); + bool operator==(const env_var_t &s) const { + return is_missing == s.is_missing && + static_cast(*this) == static_cast(s); } - bool operator==(const wcstring &s) const - { - return ! is_missing && static_cast(*this) == s; + bool operator==(const wcstring &s) const { + return !is_missing && static_cast(*this) == s; } - bool operator!=(const env_var_t &s) const - { - return !(*this == s); + bool operator!=(const env_var_t &s) const { return !(*this == s); } + + bool operator!=(const wcstring &s) const { return !(*this == s); } + + bool operator==(const wchar_t *s) const { + return !is_missing && static_cast(*this) == s; } - bool operator!=(const wcstring &s) const - { - return !(*this == s); - } - - bool operator==(const wchar_t *s) const - { - return ! is_missing && static_cast(*this) == s; - } - - bool operator!=(const wchar_t *s) const - { - return !(*this == s); - } - - + bool operator!=(const wchar_t *s) const { return !(*this == s); } }; -/** - Gets the variable with the specified name, or env_var_t::missing_var if it does not exist or is an empty array. - - \param key The name of the variable to get - \param mode An optional scope to search in. All scopes are searched if unset -*/ +/// Gets the variable with the specified name, or env_var_t::missing_var if it does not exist or is +/// an empty array. +/// +/// \param key The name of the variable to get +/// \param mode An optional scope to search in. All scopes are searched if unset env_var_t env_get_string(const wcstring &key, env_mode_flags_t mode = ENV_DEFAULT); -/** - Returns true if the specified key exists. This can't be reliably done - using env_get, since env_get returns null for 0-element arrays - - \param key The name of the variable to remove - \param mode the scope to search in. All scopes are searched if set to default -*/ +/// Returns true if the specified key exists. This can't be reliably done using env_get, since +/// env_get returns null for 0-element arrays. +/// +/// \param key The name of the variable to remove +/// \param mode the scope to search in. All scopes are searched if set to default bool env_exist(const wchar_t *key, env_mode_flags_t mode); -/** - Remove environemnt variable - - \param key The name of the variable to remove - \param mode should be ENV_USER if this is a remove request from the user, 0 otherwise. If this is a user request, read-only variables can not be removed. The mode may also specify the scope of the variable that should be erased. - - \return zero if the variable existed, and non-zero if the variable did not exist -*/ +/// Remove environment variable. +/// +/// \param key The name of the variable to remove +/// \param mode should be ENV_USER if this is a remove request from the user, 0 otherwise. If this +/// is a user request, read-only variables can not be removed. The mode may also specify the scope +/// of the variable that should be erased. +/// +/// \return zero if the variable existed, and non-zero if the variable did not exist int env_remove(const wcstring &key, int mode); -/** - Push the variable stack. Used for implementing local variables for functions and for-loops. -*/ +/// Push the variable stack. Used for implementing local variables for functions and for-loops. void env_push(bool new_scope); -/** - Pop the variable stack. Used for implementing local variables for functions and for-loops. -*/ +/// Pop the variable stack. Used for implementing local variables for functions and for-loops. void env_pop(); -/** Synchronizes all universal variable changes: writes everything out, reads stuff in */ +/// Synchronizes all universal variable changes: writes everything out, reads stuff in. void env_universal_barrier(); -/** Returns an array containing all exported variables in a format suitable for execv. */ -const char * const * env_export_arr(bool recalc); +/// Returns an array containing all exported variables in a format suitable for execv. +const char *const *env_export_arr(bool recalc); -/** Sets up argv as the given null terminated array of strings */ -void env_set_argv(const wchar_t * const * argv); +/// Sets up argv as the given null terminated array of strings. +void env_set_argv(const wchar_t *const *argv); -/** - Returns all variable names. -*/ +/// Returns all variable names. wcstring_list_t env_get_names(int flags); -/** Update the PWD variable directory */ +/// Update the PWD variable directory. int env_set_pwd(); -/* Returns the PWD with a terminating slash */ +/// Returns the PWD with a terminating slash. wcstring env_get_pwd_slash(); -class env_vars_snapshot_t -{ +class env_vars_snapshot_t { std::map vars; bool is_current() const; - - env_vars_snapshot_t(const env_vars_snapshot_t&); + + env_vars_snapshot_t(const env_vars_snapshot_t &); void operator=(const env_vars_snapshot_t &); - -public: - env_vars_snapshot_t(const wchar_t * const * keys); + + public: + env_vars_snapshot_t(const wchar_t *const *keys); env_vars_snapshot_t(); env_var_t get(const wcstring &key) const; - // Returns the fake snapshot representing the live variables array + // Returns the fake snapshot representing the live variables array. static const env_vars_snapshot_t ¤t(); - // vars necessary for highlighting - static const wchar_t * const highlighting_keys[]; - - // vars necessary for completion - static const wchar_t * const completing_keys[]; + // Vars necessary for highlighting. + static const wchar_t *const highlighting_keys[]; + + // Vars necessary for completion. + static const wchar_t *const completing_keys[]; }; extern bool g_log_forks; @@ -252,18 +198,13 @@ extern int g_fork_count; extern bool g_use_posix_spawn; -/** - A variable entry. Stores the value of a variable and whether it - should be exported. - */ -struct var_entry_t -{ - wcstring val; /**< The value of the variable */ - bool exportv; /**< Whether the variable should be exported */ - - var_entry_t() : exportv(false) { } +/// A variable entry. Stores the value of a variable and whether it should be exported. +struct var_entry_t { + wcstring val; // the value of the variable + bool exportv; // whether the variable should be exported + + var_entry_t() : exportv(false) {} }; typedef std::map var_table_t; - #endif From 690ceeeaa79eb47ceb32562fe11ebd9e953a9d24 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Thu, 28 Apr 2016 18:55:41 -0700 Subject: [PATCH 155/363] restyle env_universal module to match project style Reduces lint errors from 121 to 52 (-57%). Line count from 1916 to 1671 (-13%). Another step in resolving issue #2902. --- src/env_universal_common.cpp | 1592 ++++++++++++++-------------------- src/env_universal_common.h | 205 +++-- 2 files changed, 777 insertions(+), 1020 deletions(-) diff --git a/src/env_universal_common.cpp b/src/env_universal_common.cpp index 8f81aff04..fc78a1ceb 100644 --- a/src/env_universal_common.cpp +++ b/src/env_universal_common.cpp @@ -1,48 +1,43 @@ -/** - \file env_universal_common.c - - The utility library for universal variables. Used both by the - client library and by the daemon. -*/ +// The utility library for universal variables. Used both by the client library and by the daemon. #include "config.h" -#include -#include -#include // IWYU pragma: keep -#include +#include // IWYU pragma: keep #include #include +#include #include +#include #include +#include #include #include +#include +#include #include #include #include #include #include #include -#include -#include #ifdef HAVE_SYS_SELECT_H #include #endif +#include #include #include -#include // We need the ioctl.h header so we can check if SIOCGIFHWADDR is defined by it so we know if we're // on a Linux system. #include // IWYU pragma: keep // We need the sys/file.h for the flock() declaration on Linux but not OS X. #include // IWYU pragma: keep -#include "env_universal_common.h" -#include "fallback.h" // IWYU pragma: keep -#include "util.h" #include "common.h" -#include "wutil.h" -#include "utf8.h" #include "env.h" +#include "env_universal_common.h" +#include "fallback.h" // IWYU pragma: keep +#include "utf8.h" +#include "util.h" +#include "wutil.h" #if __APPLE__ #define FISH_NOTIFYD_AVAILABLE 1 @@ -56,111 +51,84 @@ #define NAME_MAX _XOPEN_NAME_MAX #endif -/** - The set command -*/ +/// The set command. #define SET_STR L"SET" -/** - The set_export command -*/ +/// The set_export command. #define SET_EXPORT_STR L"SET_EXPORT" - -/** - Non-wide version of the set command -*/ +/// Non-wide version of the set command. #define SET_MBS "SET" -/** - Non-wide version of the set_export command -*/ +/// Non-wide version of the set_export command. #define SET_EXPORT_MBS "SET_EXPORT" -/** - Error message -*/ +/// Error message. #define PARSE_ERR L"Unable to parse universal variable message: '%ls'" -/** Small note about not editing ~/.fishd manually. Inserted at the top of all .fishd files. */ -#define SAVE_MSG "# This file is automatically generated by the fish.\n# Do NOT edit it directly, your changes will be overwritten.\n" +/// Small note about not editing ~/.fishd manually. Inserted at the top of all .fishd files. +#define SAVE_MSG \ + "# This file is automatically generated by the fish.\n# Do NOT edit it directly, your " \ + "changes will be overwritten.\n" static wcstring fishd_get_config(); static wcstring get_machine_identifier(); static bool get_hostname_identifier(wcstring *result); -static wcstring vars_filename_in_directory(const wcstring &wdir) -{ - if (wdir.empty()) - return L""; - +static wcstring vars_filename_in_directory(const wcstring &wdir) { + if (wdir.empty()) return L""; + wcstring result = wdir; result.append(L"/fishd."); result.append(get_machine_identifier()); return result; } -static const wcstring &default_vars_path() -{ +static const wcstring &default_vars_path() { static wcstring cached_result = vars_filename_in_directory(fishd_get_config()); return cached_result; } -/** - Check, and create if necessary, a secure runtime path - Derived from tmux.c in tmux (http://tmux.sourceforge.net/) -*/ -static int check_runtime_path(const char * path) -{ - /* - * Copyright (c) 2007 Nicholas Marriott - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING - * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - +/// Check, and create if necessary, a secure runtime path Derived from tmux.c in tmux +/// (http://tmux.sourceforge.net/). +static int check_runtime_path(const char *path) { + // Copyright (c) 2007 Nicholas Marriott + // + // Permission to use, copy, modify, and distribute this software for any + // purpose with or without fee is hereby granted, provided that the above + // copyright notice and this permission notice appear in all copies. + // + // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + // WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + // IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + // OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. struct stat statpath; uid_t uid = geteuid(); - if (mkdir(path, S_IRWXU) != 0 && errno != EEXIST) - return errno; - if (lstat(path, &statpath) != 0) - return errno; - if (!S_ISDIR(statpath.st_mode) - || statpath.st_uid != uid - || (statpath.st_mode & (S_IRWXG|S_IRWXO)) != 0) + if (mkdir(path, S_IRWXU) != 0 && errno != EEXIST) return errno; + if (lstat(path, &statpath) != 0) return errno; + if (!S_ISDIR(statpath.st_mode) || statpath.st_uid != uid || + (statpath.st_mode & (S_IRWXG | S_IRWXO)) != 0) return EACCES; return 0; } -/** Return the path of an appropriate runtime data directory */ -static wcstring get_runtime_path() -{ +/// Return the path of an appropriate runtime data directory. +static wcstring get_runtime_path() { wcstring result; const char *dir = getenv("XDG_RUNTIME_DIR"); - // Check that the path is actually usable - // Technically this is guaranteed by the fdo spec but in practice - // it is not always the case: see #1828 and #2222 + // Check that the path is actually usable. Technically this is guaranteed by the fdo spec but in + // practice it is not always the case: see #1828 and #2222. int mode = R_OK | W_OK | X_OK; - if (dir != NULL && access(dir, mode) == 0 && check_runtime_path(dir) == 0) - { + if (dir != NULL && access(dir, mode) == 0 && check_runtime_path(dir) == 0) { result = str2wcstring(dir); - } - else - { + } else { const char *uname = getenv("USER"); - if (uname == NULL) - { + if (uname == NULL) { const struct passwd *pw = getpwuid(getuid()); uname = pw->pw_name; } @@ -168,371 +136,310 @@ static wcstring get_runtime_path() // /tmp/fish.user std::string tmpdir = "/tmp/fish."; tmpdir.append(uname); - if (check_runtime_path(tmpdir.c_str()) != 0) - { - debug(0, L"Runtime path not available. Try deleting the directory %s and restarting fish.", tmpdir.c_str()); - } - else - { + if (check_runtime_path(tmpdir.c_str()) != 0) { + debug(0, + L"Runtime path not available. Try deleting the directory %s and restarting fish.", + tmpdir.c_str()); + } else { result = str2wcstring(tmpdir); } } return result; } - -/* Returns a "variables" file in the appropriate runtime directory. This is called infrequently and so does not need to be cached. */ -static wcstring default_named_pipe_path() -{ - // Note that vars_filename_in_directory returns empty string when passed the empty string +/// Returns a "variables" file in the appropriate runtime directory. This is called infrequently and +/// so does not need to be cached. +static wcstring default_named_pipe_path() { + // Note that vars_filename_in_directory returns empty string when passed the empty string. return vars_filename_in_directory(get_runtime_path()); } -/** - Test if the message msg contains the command cmd -*/ -static bool match(const wchar_t *msg, const wchar_t *cmd) -{ +/// Test if the message msg contains the command cmd. +static bool match(const wchar_t *msg, const wchar_t *cmd) { size_t len = wcslen(cmd); - if (wcsncasecmp(msg, cmd, len) != 0) - return false; + if (wcsncasecmp(msg, cmd, len) != 0) return false; - if (msg[len] && msg[len]!= L' ' && msg[len] != L'\t') - return false; + if (msg[len] && msg[len] != L' ' && msg[len] != L'\t') return false; return true; } -static void report_error(int err_code, const wchar_t *err_format, ...) -{ +static void report_error(int err_code, const wchar_t *err_format, ...) { va_list va; va_start(va, err_format); const wcstring err_text = vformat_string(err_format, va); va_end(va); - - if (! err_text.empty()) - { + + if (!err_text.empty()) { fwprintf(stderr, L"%ls: ", err_text.c_str()); } fwprintf(stderr, L"%s\n", strerror(err_code)); } -/* The universal variable format has some funny escaping requirements; here we try to be safe */ -static bool is_universal_safe_to_encode_directly(wchar_t c) -{ - if (c < 32 || c > 128) - return false; +/// The universal variable format has some funny escaping requirements; here we try to be safe. +static bool is_universal_safe_to_encode_directly(wchar_t c) { + if (c < 32 || c > 128) return false; return iswalnum(c) || wcschr(L"/_", c); } -/** - Escape specified string - */ -static wcstring full_escape(const wchar_t *in) -{ +/// Escape specified string. +static wcstring full_escape(const wchar_t *in) { wcstring out; - for (; *in; in++) - { + for (; *in; in++) { wchar_t c = *in; - if (is_universal_safe_to_encode_directly(c)) - { + if (is_universal_safe_to_encode_directly(c)) { out.push_back(c); - } - else if (c <= (wchar_t)ASCII_MAX) - { - // See #1225 for discussion of use of ASCII_MAX here + } else if (c <= (wchar_t)ASCII_MAX) { + // See #1225 for discussion of use of ASCII_MAX here. append_format(out, L"\\x%.2x", c); - } - else if (c < 65536) - { + } else if (c < 65536) { append_format(out, L"\\u%.4x", c); - } - else - { + } else { append_format(out, L"\\U%.8x", c); } } return out; } -/* Converts input to UTF-8 and appends it to receiver, using storage as temp storage */ -static bool append_utf8(const wcstring &input, std::string *receiver, std::string *storage) -{ +/// Converts input to UTF-8 and appends it to receiver, using storage as temp storage. +static bool append_utf8(const wcstring &input, std::string *receiver, std::string *storage) { bool result = false; - if (wchar_to_utf8_string(input, storage)) - { + if (wchar_to_utf8_string(input, storage)) { receiver->append(*storage); result = true; } return result; } -/* Creates a file entry like "SET fish_color_cwd:FF0". Appends the result to *result (as UTF8). Returns true on success. storage may be used for temporary storage, to avoid allocations */ -static bool append_file_entry(fish_message_type_t type, const wcstring &key_in, const wcstring &val_in, std::string *result, std::string *storage) -{ +/// Creates a file entry like "SET fish_color_cwd:FF0". Appends the result to *result (as UTF8). +/// Returns true on success. storage may be used for temporary storage, to avoid allocations. +static bool append_file_entry(fish_message_type_t type, const wcstring &key_in, + const wcstring &val_in, std::string *result, std::string *storage) { assert(storage != NULL); assert(result != NULL); - - // Record the length on entry, in case we need to back up + + // Record the length on entry, in case we need to back up. bool success = true; const size_t result_length_on_entry = result->size(); - + // Append header like "SET " - result->append(type==SET ? SET_MBS : SET_EXPORT_MBS); + result->append(type == SET ? SET_MBS : SET_EXPORT_MBS); result->push_back(' '); - // Append variable name like "fish_color_cwd" - if (wcsvarname(key_in.c_str())) - { + // Append variable name like "fish_color_cwd". + if (wcsvarname(key_in.c_str())) { debug(0, L"Illegal variable name: '%ls'", key_in.c_str()); success = false; } - if (success && ! append_utf8(key_in, result, storage)) - { + if (success && !append_utf8(key_in, result, storage)) { debug(0, L"Could not convert %ls to narrow character string", key_in.c_str()); success = false; } - - // Append ":" - if (success) - { + + // Append ":". + if (success) { result->push_back(':'); } - - // Append value - if (success && ! append_utf8(full_escape(val_in.c_str()), result, storage)) - { + + // Append value. + if (success && !append_utf8(full_escape(val_in.c_str()), result, storage)) { debug(0, L"Could not convert %ls to narrow character string", val_in.c_str()); success = false; } - - // Append newline - if (success) - { + + // Append newline. + if (success) { result->push_back('\n'); } - - // Don't modify result on failure. It's sufficient to simply resize it since all we ever did was append to it. - if (! success) - { + + // Don't modify result on failure. It's sufficient to simply resize it since all we ever did was + // append to it. + if (!success) { result->resize(result_length_on_entry); } - + return success; } -env_universal_t::env_universal_t(const wcstring &path) : explicit_vars_path(path), tried_renaming(false), last_read_file(kInvalidFileID) -{ +env_universal_t::env_universal_t(const wcstring &path) + : explicit_vars_path(path), tried_renaming(false), last_read_file(kInvalidFileID) { VOMIT_ON_FAILURE(pthread_mutex_init(&lock, NULL)); } -env_universal_t::~env_universal_t() -{ - pthread_mutex_destroy(&lock); -} +env_universal_t::~env_universal_t() { pthread_mutex_destroy(&lock); } -env_var_t env_universal_t::get(const wcstring &name) const -{ +env_var_t env_universal_t::get(const wcstring &name) const { env_var_t result = env_var_t::missing_var(); var_table_t::const_iterator where = vars.find(name); - if (where != vars.end()) - { + if (where != vars.end()) { result = env_var_t(where->second.val); } return result; } -bool env_universal_t::get_export(const wcstring &name) const -{ +bool env_universal_t::get_export(const wcstring &name) const { bool result = false; var_table_t::const_iterator where = vars.find(name); - if (where != vars.end()) - { + if (where != vars.end()) { result = where->second.exportv; } return result; } - -void env_universal_t::set_internal(const wcstring &key, const wcstring &val, bool exportv, bool overwrite) -{ +void env_universal_t::set_internal(const wcstring &key, const wcstring &val, bool exportv, + bool overwrite) { ASSERT_IS_LOCKED(lock); - if (! overwrite && this->modified.find(key) != this->modified.end()) - { - /* This value has been modified and we're not overwriting it. Skip it. */ + if (!overwrite && this->modified.find(key) != this->modified.end()) { + // This value has been modified and we're not overwriting it. Skip it. return; } - + var_entry_t *entry = &vars[key]; - if (entry->exportv != exportv || entry->val != val) - { + if (entry->exportv != exportv || entry->val != val) { entry->val = val; entry->exportv = exportv; - - /* If we are overwriting, then this is now modified */ - if (overwrite) - { + + // If we are overwriting, then this is now modified. + if (overwrite) { this->modified.insert(key); } } } -void env_universal_t::set(const wcstring &key, const wcstring &val, bool exportv) -{ +void env_universal_t::set(const wcstring &key, const wcstring &val, bool exportv) { scoped_lock locker(lock); this->set_internal(key, val, exportv, true /* overwrite */); } -bool env_universal_t::remove_internal(const wcstring &key) -{ +bool env_universal_t::remove_internal(const wcstring &key) { ASSERT_IS_LOCKED(lock); size_t erased = this->vars.erase(key); - if (erased > 0) - { + if (erased > 0) { this->modified.insert(key); } return erased > 0; } -bool env_universal_t::remove(const wcstring &key) -{ +bool env_universal_t::remove(const wcstring &key) { scoped_lock locker(lock); return this->remove_internal(key); } -wcstring_list_t env_universal_t::get_names(bool show_exported, bool show_unexported) const -{ +wcstring_list_t env_universal_t::get_names(bool show_exported, bool show_unexported) const { wcstring_list_t result; scoped_lock locker(lock); var_table_t::const_iterator iter; - for (iter = vars.begin(); iter != vars.end(); ++iter) - { + for (iter = vars.begin(); iter != vars.end(); ++iter) { const wcstring &key = iter->first; const var_entry_t &e = iter->second; - if ((e.exportv && show_exported) || (! e.exportv && show_unexported)) - { + if ((e.exportv && show_exported) || (!e.exportv && show_unexported)) { result.push_back(key); } } return result; } -/* Given a variable table, generate callbacks representing the difference between our vars and the new vars */ -void env_universal_t::generate_callbacks(const var_table_t &new_vars, callback_data_list_t *callbacks) const -{ +// Given a variable table, generate callbacks representing the difference between our vars and the +// new vars. +void env_universal_t::generate_callbacks(const var_table_t &new_vars, + callback_data_list_t *callbacks) const { assert(callbacks != NULL); - - /* Construct callbacks for erased values */ - for (var_table_t::const_iterator iter = this->vars.begin(); iter != this->vars.end(); ++iter) - { + + // Construct callbacks for erased values. + for (var_table_t::const_iterator iter = this->vars.begin(); iter != this->vars.end(); ++iter) { const wcstring &key = iter->first; - - /* Skip modified values */ - if (this->modified.find(key) != this->modified.end()) - { + + // Skip modified values. + if (this->modified.find(key) != this->modified.end()) { continue; } - - /* If the value is not present in new_vars, it has been erased */ - if (new_vars.find(key) == new_vars.end()) - { + + // If the value is not present in new_vars, it has been erased. + if (new_vars.find(key) == new_vars.end()) { callbacks->push_back(callback_data_t(ERASE, key, L"")); } } - - /* Construct callbacks for newly inserted or changed values */ - for (var_table_t::const_iterator iter = new_vars.begin(); iter != new_vars.end(); ++iter) - { + + // Construct callbacks for newly inserted or changed values. + for (var_table_t::const_iterator iter = new_vars.begin(); iter != new_vars.end(); ++iter) { const wcstring &key = iter->first; - - /* Skip modified values */ - if (this->modified.find(key) != this->modified.end()) - { + + // Skip modified values. + if (this->modified.find(key) != this->modified.end()) { continue; } - - /* See if the value has changed */ + + // See if the value has changed. const var_entry_t &new_entry = iter->second; var_table_t::const_iterator existing = this->vars.find(key); - if (existing == this->vars.end() || existing->second.exportv != new_entry.exportv || existing->second.val != new_entry.val) - { - /* Value has changed */ - callbacks->push_back(callback_data_t(new_entry.exportv ? SET_EXPORT : SET, key, new_entry.val)); + if (existing == this->vars.end() || existing->second.exportv != new_entry.exportv || + existing->second.val != new_entry.val) { + // Value has changed. + callbacks->push_back( + callback_data_t(new_entry.exportv ? SET_EXPORT : SET, key, new_entry.val)); } } } - -void env_universal_t::acquire_variables(var_table_t *vars_to_acquire) -{ - /* Copy modified values from existing vars to vars_to_acquire */ - for (std::set::iterator iter = this->modified.begin(); iter != this->modified.end(); ++iter) - { +void env_universal_t::acquire_variables(var_table_t *vars_to_acquire) { + // Copy modified values from existing vars to vars_to_acquire. + for (std::set::iterator iter = this->modified.begin(); iter != this->modified.end(); + ++iter) { const wcstring &key = *iter; var_table_t::iterator src_iter = this->vars.find(key); - if (src_iter == this->vars.end()) - { + if (src_iter == this->vars.end()) { /* The value has been deleted. */ vars_to_acquire->erase(key); - } - else - { - /* The value has been modified. Copy it over. Note we can destructively modify the source entry in vars since we are about to get rid of this->vars entirely. */ + } else { + // The value has been modified. Copy it over. Note we can destructively modify the + // source entry in vars since we are about to get rid of this->vars entirely. var_entry_t &src = src_iter->second; var_entry_t &dst = (*vars_to_acquire)[key]; dst.val.swap(src.val); dst.exportv = src.exportv; } } - - /* We have constructed all the callbacks and updated vars_to_acquire. Acquire it! */ + + // We have constructed all the callbacks and updated vars_to_acquire. Acquire it! this->vars.swap(*vars_to_acquire); } -void env_universal_t::load_from_fd(int fd, callback_data_list_t *callbacks) -{ +void env_universal_t::load_from_fd(int fd, callback_data_list_t *callbacks) { ASSERT_IS_LOCKED(lock); assert(fd >= 0); - /* Get the dev / inode */ + // Get the dev / inode. const file_id_t current_file = file_id_for_fd(fd); - if (current_file == last_read_file) - { + if (current_file == last_read_file) { debug(5, L"universal log sync elided based on fstat()"); - } - else - { - /* Read a variables table from the file. */ + } else { + // Read a variables table from the file. var_table_t new_vars = this->read_message_internal(fd); - - /* Announce changes */ - if (callbacks != NULL) - { + + // Announce changes. + if (callbacks != NULL) { this->generate_callbacks(new_vars, callbacks); } - - /* Acquire the new variables */ + + // Acquire the new variables. this->acquire_variables(&new_vars); - last_read_file = current_file; } } -bool env_universal_t::load_from_path(const wcstring &path, callback_data_list_t *callbacks) -{ +bool env_universal_t::load_from_path(const wcstring &path, callback_data_list_t *callbacks) { ASSERT_IS_LOCKED(lock); - - /* Check to see if the file is unchanged. We do this again in load_from_fd, but this avoids opening the file unnecessarily. */ - if (last_read_file != kInvalidFileID && file_id_for_path(path) == last_read_file) - { + + // Check to see if the file is unchanged. We do this again in load_from_fd, but this avoids + // opening the file unnecessarily. + if (last_read_file != kInvalidFileID && file_id_for_path(path) == last_read_file) { debug(5, L"universal log sync elided based on fast stat()"); return true; } - + bool result = false; int fd = wopen_cloexec(path, O_RDONLY); - if (fd >= 0) - { + if (fd >= 0) { debug(5, L"universal log reading from file"); this->load_from_fd(fd, callbacks); close(fd); @@ -541,121 +448,110 @@ bool env_universal_t::load_from_path(const wcstring &path, callback_data_list_t return result; } -/* Writes our state to the fd. path is provided only for error reporting */ -bool env_universal_t::write_to_fd(int fd, const wcstring &path) -{ +/// Writes our state to the fd. path is provided only for error reporting. +bool env_universal_t::write_to_fd(int fd, const wcstring &path) { ASSERT_IS_LOCKED(lock); assert(fd >= 0); bool success = true; - // Stuff we output to fd + // Stuff we output to fd. std::string contents; - - // Temporary storage + + // Temporary storage. std::string storage; - + // Write the save message. If this fails, we don't bother complaining. write_loop(fd, SAVE_MSG, strlen(SAVE_MSG)); - + var_table_t::const_iterator iter = vars.begin(); - while (iter != vars.end()) - { - // Append the entry. Note that append_file_entry may fail, but that only affects one variable; soldier on. + while (iter != vars.end()) { + // Append the entry. Note that append_file_entry may fail, but that only affects one + // variable; soldier on. const wcstring &key = iter->first; const var_entry_t &entry = iter->second; append_file_entry(entry.exportv ? SET_EXPORT : SET, key, entry.val, &contents, &storage); - - // Go to next + + // Go to next. ++iter; - - // Flush if this is the last iteration or we exceed a page - if (iter == vars.end() || contents.size() >= 4096) - { - if (write_loop(fd, contents.data(), contents.size()) < 0) - { + + // Flush if this is the last iteration or we exceed a page. + if (iter == vars.end() || contents.size() >= 4096) { + if (write_loop(fd, contents.data(), contents.size()) < 0) { int err = errno; - report_error(err, L"Unable to write to universal variables file '%ls'", path.c_str()); + report_error(err, L"Unable to write to universal variables file '%ls'", + path.c_str()); success = false; break; } contents.clear(); } } - - /* Since we just wrote out this file, it matches our internal state; pretend we read from it */ + + // Since we just wrote out this file, it matches our internal state; pretend we read from it. this->last_read_file = file_id_for_fd(fd); - - /* We don't close the file */ + + // We don't close the file. return success; } -bool env_universal_t::move_new_vars_file_into_place(const wcstring &src, const wcstring &dst) -{ +bool env_universal_t::move_new_vars_file_into_place(const wcstring &src, const wcstring &dst) { int ret = wrename(src, dst); - if (ret != 0) - { + if (ret != 0) { int err = errno; report_error(err, L"Unable to rename file from '%ls' to '%ls'", src.c_str(), dst.c_str()); } return ret == 0; } -static wcstring fishd_get_config() -{ +static wcstring fishd_get_config() { bool done = false; wcstring result; env_var_t xdg_dir = env_get_string(L"XDG_CONFIG_HOME", ENV_GLOBAL | ENV_EXPORT); - if (! xdg_dir.missing_or_empty()) - { + if (!xdg_dir.missing_or_empty()) { result = xdg_dir; append_path_component(result, L"/fish"); - if (!create_directory(result)) - { + if (!create_directory(result)) { done = true; } - } - else - { + } else { env_var_t home = env_get_string(L"HOME", ENV_GLOBAL | ENV_EXPORT); - if (! home.missing_or_empty()) - { + if (!home.missing_or_empty()) { result = home; append_path_component(result, L"/.config/fish"); - if (!create_directory(result)) - { + if (!create_directory(result)) { done = 1; } } } - - 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.")); + + if (!done) { + // Bad juju. + debug(0, _(L"Unable to create a configuration directory for fish. Your personal settings " + L"will not be saved. Please set the $XDG_CONFIG_HOME variable to a directory " + L"where the current user has write access.")); result.clear(); } - + return result; } -bool env_universal_t::load() -{ +bool env_universal_t::load() { scoped_lock locker(lock); callback_data_list_t callbacks; - const wcstring vars_path = explicit_vars_path.empty() ? default_vars_path() : explicit_vars_path; + const wcstring vars_path = + explicit_vars_path.empty() ? default_vars_path() : explicit_vars_path; bool success = load_from_path(vars_path, &callbacks); - if (! success && ! tried_renaming && errno == ENOENT) - { - /* We failed to load, because the file was not found. Older fish used the hostname only. Try *moving* the filename based on the hostname into place; if that succeeds try again. Silently "upgraded." */ + if (!success && !tried_renaming && errno == ENOENT) { + // We failed to load, because the file was not found. Older fish used the hostname only. Try + // moving the filename based on the hostname into place; if that succeeds try again. + // Silently "upgraded." tried_renaming = true; wcstring hostname_id; - if (get_hostname_identifier(&hostname_id)) - { + if (get_hostname_identifier(&hostname_id)) { const wcstring hostname_path = wdirname(vars_path) + L'/' + hostname_id; - if (0 == wrename(hostname_path, vars_path)) - { - /* We renamed - try again */ + if (0 == wrename(hostname_path, vars_path)) { + // We renamed - try again. success = this->load(); } } @@ -664,8 +560,7 @@ bool env_universal_t::load() } bool env_universal_t::open_temporary_file(const wcstring &directory, wcstring *out_path, - int *out_fd) -{ + int *out_fd) { // Create and open a temporary file for writing within the given directory. Try to create a // temporary file, up to 10 times. We don't use mkstemps because we want to open it CLO_EXEC. // This should almost always succeed on the first try. @@ -676,8 +571,7 @@ bool env_universal_t::open_temporary_file(const wcstring &directory, wcstring *o const wcstring tmp_name_template = directory + L"/fishd.tmp.XXXXXX"; wcstring tmp_name; - for (size_t attempt = 0; attempt < 10 && ! success; attempt++) - { + for (size_t attempt = 0; attempt < 10 && !success; attempt++) { int result_fd = -1; char *narrow_str = wcs2str(tmp_name_template.c_str()); #if HAVE_MKOSTEMP @@ -685,8 +579,7 @@ bool env_universal_t::open_temporary_file(const wcstring &directory, wcstring *o #else // cppcheck-suppress redundantAssignment result_fd = mkstemp(narrow_str); - if (result_fd != -1) - { + if (result_fd != -1) { fcntl(result_fd, F_SETFD, FD_CLOEXEC); } #endif @@ -698,19 +591,19 @@ bool env_universal_t::open_temporary_file(const wcstring &directory, wcstring *o free(narrow_str); } - if (!success) - { + if (!success) { report_error(saved_errno, L"Unable to open file '%ls'", out_path->c_str()); } return success; } -bool env_universal_t::open_and_acquire_lock(const wcstring &path, int *out_fd) -{ - /* Attempt to open the file for reading at the given path, atomically acquiring a lock. On BSD, we can use O_EXLOCK. On Linux, we open the file, take a lock, and then compare fstat() to stat(); if they match, it means that the file was not replaced before we acquired the lock. - - We pass O_RDONLY with O_CREAT; this creates a potentially empty file. We do this so that we have something to lock on. - */ +bool env_universal_t::open_and_acquire_lock(const wcstring &path, int *out_fd) { + // Attempt to open the file for reading at the given path, atomically acquiring a lock. On BSD, + // we can use O_EXLOCK. On Linux, we open the file, take a lock, and then compare fstat() to + // stat(); if they match, it means that the file was not replaced before we acquired the lock. + // + // We pass O_RDONLY with O_CREAT; this creates a potentially empty file. We do this so that we + // have something to lock on. int result_fd = -1; bool needs_lock = true; int flags = O_RDWR | O_CREAT; @@ -718,337 +611,304 @@ bool env_universal_t::open_and_acquire_lock(const wcstring &path, int *out_fd) flags |= O_EXLOCK; needs_lock = false; #endif - for (;;) - { + for (;;) { int fd = wopen_cloexec(path, flags, 0644); - if (fd < 0) - { + if (fd < 0) { int err = errno; - if (err == EINTR) - { + if (err == EINTR) { /* Signal; try again */ continue; } #ifdef O_EXLOCK - else if (err == EOPNOTSUPP) - { - /* Filesystem probably does not support locking. Clear the flag and try again. Note that we try taking the lock via flock anyways. */ + else if (err == EOPNOTSUPP) { + // Filesystem probably does not support locking. Clear the flag and try again. Note + // that we try taking the lock via flock anyways. flags &= ~O_EXLOCK; needs_lock = true; continue; } #endif - else - { + else { report_error(err, L"Unable to open universal variable file '%ls'", path.c_str()); break; } } - - /* If we get here, we must have a valid fd */ + + // If we get here, we must have a valid fd. assert(fd >= 0); - - /* Try taking the lock, if necessary. If we failed, we may be on lockless NFS, etc.; in that case we pretend we succeeded. See the comment in save_to_path for the rationale. */ - if (needs_lock) - { - while (flock(fd, LOCK_EX) < 0) - { + + // Try taking the lock, if necessary. If we failed, we may be on lockless NFS, etc.; in that + // case we pretend we succeeded. See the comment in save_to_path for the rationale. + if (needs_lock) { + while (flock(fd, LOCK_EX) < 0) { /* error */ - if (errno != EINTR) - { + if (errno != EINTR) { /* Do nothing per #2149 */ break; } } } - - /* Hopefully we got the lock. However, it's possible the file changed out from under us while we were waiting for the lock. Make sure that didn't happen. */ - if (file_id_for_fd(fd) != file_id_for_path(path)) - { - /* Oops, it changed! Try again */ + + // Hopefully we got the lock. However, it's possible the file changed out from under us + // while we were waiting for the lock. Make sure that didn't happen. + if (file_id_for_fd(fd) != file_id_for_path(path)) { + // Oops, it changed! Try again. close(fd); continue; } - - /* Finally, we have an fd that's valid and hopefully locked. We're done */ + // Finally, we have an fd that's valid and hopefully locked. We're done. assert(fd >= 0); result_fd = fd; break; } - + *out_fd = result_fd; - + return result_fd >= 0; } -/* Returns true if modified variables were written, false if not. (There may still be variable changes due to other processes on a false return). */ -bool env_universal_t::sync(callback_data_list_t *callbacks) -{ +// Returns true if modified variables were written, false if not. (There may still be variable +// changes due to other processes on a false return). +bool env_universal_t::sync(callback_data_list_t *callbacks) { debug(5, L"universal log sync"); scoped_lock locker(lock); - /* Our saving strategy: - - 1. Open the file, producing an fd. - 2. Lock the file (may be combined with step 1 on systems with O_EXLOCK) - 3. After taking the lock, check if the file at the given path is different from what we opened. If so, start over. - 4. Read from the file. This can be elided if its dev/inode is unchanged since the last read - 5. Open an adjacent temporary file - 6. Write our changes to an adjacent file - 7. Move the adjacent file into place via rename. This is assumed to be atomic. - 8. Release the lock and close the file - - Consider what happens if Process 1 and 2 both do this simultaneously. Can there be data loss? Process 1 opens the file and then attempts to take the lock. Now, either process 1 will see the original file, or process 2's new file. If it sees the new file, we're OK: it's going to read from the new file, and so there's no data loss. If it sees the old file, then process 2 must have locked it (if process 1 locks it, switch their roles). The lock will block until process 2 reaches step 7; at that point process 1 will reach step 2, notice that the file has changed, and then start over. - - It's possible that the underlying filesystem does not support locks (lockless NFS). In this case, we risk data loss if two shells try to write their universal variables simultaneously. In practice this is unlikely, since uvars are usually written interactively. - - Prior versions of fish used a hard link scheme to support file locking on lockless NFS. The risk here is that if the process crashes or is killed while holding the lock, future instances of fish will not be able to obtain it. This seems to be a greater risk than that of data loss on lockless NFS. Users who put their home directory on lockless NFS are playing with fire anyways. - */ - const wcstring &vars_path = explicit_vars_path.empty() ? default_vars_path() : explicit_vars_path; + // Our saving strategy: + // + // 1. Open the file, producing an fd. + // 2. Lock the file (may be combined with step 1 on systems with O_EXLOCK) + // 3. After taking the lock, check if the file at the given path is different from what we + // opened. If so, start over. + // 4. Read from the file. This can be elided if its dev/inode is unchanged since the last read + // 5. Open an adjacent temporary file + // 6. Write our changes to an adjacent file + // 7. Move the adjacent file into place via rename. This is assumed to be atomic. + // 8. Release the lock and close the file + // + // Consider what happens if Process 1 and 2 both do this simultaneously. Can there be data loss? + // Process 1 opens the file and then attempts to take the lock. Now, either process 1 will see + // the original file, or process 2's new file. If it sees the new file, we're OK: it's going to + // read from the new file, and so there's no data loss. If it sees the old file, then process 2 + // must have locked it (if process 1 locks it, switch their roles). The lock will block until + // process 2 reaches step 7; at that point process 1 will reach step 2, notice that the file has + // changed, and then start over. + // + // It's possible that the underlying filesystem does not support locks (lockless NFS). In this + // case, we risk data loss if two shells try to write their universal variables simultaneously. + // In practice this is unlikely, since uvars are usually written interactively. + // + // Prior versions of fish used a hard link scheme to support file locking on lockless NFS. The + // risk here is that if the process crashes or is killed while holding the lock, future + // instances of fish will not be able to obtain it. This seems to be a greater risk than that of + // data loss on lockless NFS. Users who put their home directory on lockless NFS are playing + // with fire anyways. + const wcstring &vars_path = + explicit_vars_path.empty() ? default_vars_path() : explicit_vars_path; if (vars_path.empty()) { debug(2, L"No universal variable path available"); return false; } - - /* If we have no changes, just load */ - if (modified.empty()) - { + + // If we have no changes, just load. + if (modified.empty()) { this->load_from_path(vars_path, callbacks); debug(5, L"universal log no modifications"); return false; } - + const wcstring directory = wdirname(vars_path); bool success = true; int vars_fd = -1; int private_fd = -1; wcstring private_file_path; - + debug(5, L"universal log performing full sync"); - - /* Open the file */ - if (success) - { + + // Open the file. + if (success) { success = this->open_and_acquire_lock(vars_path, &vars_fd); - if (! success) debug(5, L"universal log open_and_acquire_lock() failed"); + if (!success) debug(5, L"universal log open_and_acquire_lock() failed"); } - /* Read from it */ - if (success) - { + // Read from it. + if (success) { assert(vars_fd >= 0); this->load_from_fd(vars_fd, callbacks); } - - /* Open adjacent temporary file */ - if (success) - { + // Open adjacent temporary file. + if (success) { success = this->open_temporary_file(directory, &private_file_path, &private_fd); - if (! success) debug(5, L"universal log open_temporary_file() failed"); + if (!success) debug(5, L"universal log open_temporary_file() failed"); } - - /* Write to it */ - if (success) - { + + // Write to it. + if (success) { assert(private_fd >= 0); success = this->write_to_fd(private_fd, private_file_path); - if (! success) debug(5, L"universal log write_to_fd() failed"); + if (!success) debug(5, L"universal log write_to_fd() failed"); } - if (success) - { - /* Ensure we maintain ownership and permissions (#2176) */ + if (success) { + // Ensure we maintain ownership and permissions (#2176). struct stat sbuf; - if (wstat(vars_path, &sbuf) >= 0) - { + if (wstat(vars_path, &sbuf) >= 0) { if (fchown(private_fd, sbuf.st_uid, sbuf.st_gid) == -1) debug(5, L"universal log fchown() failed"); - if (fchmod(private_fd, sbuf.st_mode) == -1) - debug(5, L"universal log fchmod() failed"); + if (fchmod(private_fd, sbuf.st_mode) == -1) debug(5, L"universal log fchmod() failed"); } - /* Linux by default stores the mtime with low precision, low enough that updates that occur in quick succession may - result in the same mtime (even the nanoseconds field). So manually set the mtime of the new file to a high-precision - clock. Note that this is only necessary because Linux aggressively reuses inodes, causing the ABA problem; on other - platforms we tend to notice the file has changed due to a different inode (or file size!) - - It's probably worth finding a simpler solution to this. The tests ran into this, but it's unlikely to affect users. - */ +// Linux by default stores the mtime with low precision, low enough that updates that occur in quick +// succession may result in the same mtime (even the nanoseconds field). So manually set the mtime +// of the new file to a high-precision clock. Note that this is only necessary because Linux +// aggressively reuses inodes, causing the ABA problem; on other platforms we tend to notice the +// file has changed due to a different inode (or file size!) +// +// It's probably worth finding a simpler solution to this. The tests ran into this, but it's +// unlikely to affect users. #if HAVE_CLOCK_GETTIME && HAVE_FUTIMENS struct timespec times[2] = {}; - times[0].tv_nsec = UTIME_OMIT; // don't change ctime - if (0 == clock_gettime(CLOCK_REALTIME, ×[1])) - { + times[0].tv_nsec = UTIME_OMIT; // don't change ctime + if (0 == clock_gettime(CLOCK_REALTIME, ×[1])) { futimens(private_fd, times); } #endif - - /* Apply new file */ + + // Apply new file. success = this->move_new_vars_file_into_place(private_file_path, vars_path); - if (! success) debug(5, L"universal log move_new_vars_file_into_place() failed"); + if (!success) debug(5, L"universal log move_new_vars_file_into_place() failed"); } - - if (success) - { - /* Since we moved the new file into place, clear the path so we don't try to unlink it */ - private_file_path.clear(); + + if (success) { + // Since we moved the new file into place, clear the path so we don't try to unlink it. + private_file_path.clear(); } - - /* Clean up */ - if (vars_fd >= 0) - { + + // Clean up. + if (vars_fd >= 0) { close(vars_fd); } - if (private_fd >= 0) - { + if (private_fd >= 0) { close(private_fd); } - if (! private_file_path.empty()) - { + if (!private_file_path.empty()) { wunlink(private_file_path); } - - if (success) - { - /* All of our modified variables have now been written out. */ + + if (success) { + // All of our modified variables have now been written out. modified.clear(); } - + return success; } -var_table_t env_universal_t::read_message_internal(int fd) -{ +var_table_t env_universal_t::read_message_internal(int fd) { var_table_t result; - - // Temp value used to avoid repeated allocations + + // Temp value used to avoid repeated allocations. wcstring storage; - - // The line we construct (and then parse) + + // The line we construct (and then parse). std::string line; wcstring wide_line; - for (;;) - { + for (;;) { // Read into a buffer. Note this is NOT null-terminated! char buffer[1024]; ssize_t amt = read_loop(fd, buffer, sizeof buffer); - if (amt <= 0) - { + if (amt <= 0) { break; } const size_t bufflen = (size_t)amt; - - // Walk over it by lines. The contents of an unterminated line will be left in 'line' for the next iteration. + + // Walk over it by lines. The contents of an unterminated line will be left in 'line' for + // the next iteration. size_t line_start = 0; - while (line_start < amt) - { - // Run until we hit a newline + while (line_start < amt) { + // Run until we hit a newline. size_t cursor = line_start; - while (cursor < bufflen && buffer[cursor] != '\n') - { + while (cursor < bufflen && buffer[cursor] != '\n') { cursor++; } - - // Copy over what we read + + // Copy over what we read. line.append(buffer + line_start, cursor - line_start); - - // Process it if it's a newline (which is true if we are before the end of the buffer) - if (cursor < bufflen && ! line.empty()) - { - if (utf8_to_wchar(line.data(), line.size(), &wide_line, 0)) - { + + // Process it if it's a newline (which is true if we are before the end of the buffer). + if (cursor < bufflen && !line.empty()) { + if (utf8_to_wchar(line.data(), line.size(), &wide_line, 0)) { env_universal_t::parse_message_internal(wide_line, &result, &storage); } line.clear(); } - - // Skip over the newline (or skip past the end) + + // Skip over the newline (or skip past the end). line_start = cursor + 1; } } - - // We make no effort to handle an unterminated last line + + // We make no effort to handle an unterminated last line. return result; } -/** - Parse message msg - */ -void env_universal_t::parse_message_internal(const wcstring &msgstr, var_table_t *vars, wcstring *storage) -{ +/// Parse message msg/ +void env_universal_t::parse_message_internal(const wcstring &msgstr, var_table_t *vars, + wcstring *storage) { const wchar_t *msg = msgstr.c_str(); - + // debug(3, L"parse_message( %ls );", msg); - - if (msg[0] == L'#') - return; - + if (msg[0] == L'#') return; + bool is_set_export = match(msg, SET_EXPORT_STR); - bool is_set = ! is_set_export && match(msg, SET_STR); - if (is_set || is_set_export) - { + bool is_set = !is_set_export && match(msg, SET_STR); + if (is_set || is_set_export) { const wchar_t *name, *tmp; const bool exportv = is_set_export; - - name = msg+(exportv?wcslen(SET_EXPORT_STR):wcslen(SET_STR)); - while (name[0] == L'\t' || name[0] == L' ') - name++; - + + name = msg + (exportv ? wcslen(SET_EXPORT_STR) : wcslen(SET_STR)); + while (name[0] == L'\t' || name[0] == L' ') name++; + tmp = wcschr(name, L':'); - if (tmp) - { - /* Use 'storage' to hold our key to avoid allocations */ + if (tmp) { + // Use 'storage' to hold our key to avoid allocations. storage->assign(name, tmp - name); const wcstring &key = *storage; - + wcstring val; - if (unescape_string(tmp + 1, &val, 0)) - { + if (unescape_string(tmp + 1, &val, 0)) { var_entry_t &entry = (*vars)[key]; entry.exportv = exportv; - entry.val.swap(val); //acquire the value + entry.val.swap(val); // acquire the value } - } - else - { + } else { debug(1, PARSE_ERR, msg); } - } - else - { + } else { debug(1, PARSE_ERR, msg); } } -/** - Maximum length of hostname. Longer hostnames are truncated - */ +/// Maximum length of hostname. Longer hostnames are truncated. #define HOSTNAME_LEN 32 -/* Length of a MAC address */ +/// Length of a MAC address. #define MAC_ADDRESS_MAX_LEN 6 - -/* Thanks to Jan Brittenson - http://lists.apple.com/archives/xcode-users/2009/May/msg00062.html - */ +// Thanks to Jan Brittenson, http://lists.apple.com/archives/xcode-users/2009/May/msg00062.html #ifdef SIOCGIFHWADDR -/* Linux */ +// Linux #include -static bool get_mac_address(unsigned char macaddr[MAC_ADDRESS_MAX_LEN], const char *interface = "eth0") -{ +static bool get_mac_address(unsigned char macaddr[MAC_ADDRESS_MAX_LEN], + const char *interface = "eth0") { bool result = false; const int dummy = socket(AF_INET, SOCK_STREAM, 0); - if (dummy >= 0) - { + if (dummy >= 0) { struct ifreq r; strncpy((char *)r.ifr_name, interface, sizeof r.ifr_name - 1); r.ifr_name[sizeof r.ifr_name - 1] = 0; - if (ioctl(dummy, SIOCGIFHWADDR, &r) >= 0) - { + if (ioctl(dummy, SIOCGIFHWADDR, &r) >= 0) { memcpy(macaddr, r.ifr_hwaddr.sa_data, MAC_ADDRESS_MAX_LEN); result = true; } @@ -1059,27 +919,22 @@ static bool get_mac_address(unsigned char macaddr[MAC_ADDRESS_MAX_LEN], const ch #elif defined(HAVE_GETIFADDRS) -/* OS X and BSD */ +// OS X and BSD #include #include -static bool get_mac_address(unsigned char macaddr[MAC_ADDRESS_MAX_LEN], const char *interface = "en0") -{ +static bool get_mac_address(unsigned char macaddr[MAC_ADDRESS_MAX_LEN], + const char *interface = "en0") { // BSD, Mac OS X struct ifaddrs *ifap; bool ok = false; - - if (getifaddrs(&ifap) == 0) - { - for (const ifaddrs *p = ifap; p; p = p->ifa_next) - { - if (p->ifa_addr->sa_family == AF_LINK) - { + + if (getifaddrs(&ifap) == 0) { + for (const ifaddrs *p = ifap; p; p = p->ifa_next) { + if (p->ifa_addr->sa_family == AF_LINK) { if (p->ifa_name && p->ifa_name[0] && - ! strcmp((const char*)p->ifa_name, interface)) - { - - const sockaddr_dl& sdl = *(sockaddr_dl*)p->ifa_addr; - + !strcmp((const char *)p->ifa_name, interface)) { + const sockaddr_dl &sdl = *(sockaddr_dl *)p->ifa_addr; + size_t alen = sdl.sdl_alen; if (alen > MAC_ADDRESS_MAX_LEN) alen = MAC_ADDRESS_MAX_LEN; memcpy(macaddr, sdl.sdl_data + sdl.sdl_nlen, alen); @@ -1095,190 +950,162 @@ static bool get_mac_address(unsigned char macaddr[MAC_ADDRESS_MAX_LEN], const ch #else -/* Unsupported */ -static bool get_mac_address(unsigned char macaddr[MAC_ADDRESS_MAX_LEN]) -{ - return false; -} +// Unsupported +static bool get_mac_address(unsigned char macaddr[MAC_ADDRESS_MAX_LEN]) { return false; } #endif -/* Function to get an identifier based on the hostname */ -static bool get_hostname_identifier(wcstring *result) -{ +/// Function to get an identifier based on the hostname. +static bool get_hostname_identifier(wcstring *result) { bool success = false; char hostname[HOSTNAME_LEN + 1] = {}; - if (gethostname(hostname, HOSTNAME_LEN) == 0) - { + if (gethostname(hostname, HOSTNAME_LEN) == 0) { result->assign(str2wcstring(hostname)); success = true; } return success; } -/* Get a sort of unique machine identifier. Prefer the MAC address; if that fails, fall back to the hostname; if that fails, pick something. */ -wcstring get_machine_identifier() -{ +/// Get a sort of unique machine identifier. Prefer the MAC address; if that fails, fall back to the +/// hostname; if that fails, pick something. +wcstring get_machine_identifier() { wcstring result; unsigned char mac_addr[MAC_ADDRESS_MAX_LEN] = {}; - if (get_mac_address(mac_addr)) - { + if (get_mac_address(mac_addr)) { result.reserve(2 * MAC_ADDRESS_MAX_LEN); - for (size_t i=0; i < MAC_ADDRESS_MAX_LEN; i++) - { + for (size_t i = 0; i < MAC_ADDRESS_MAX_LEN; i++) { append_format(result, L"%02x", mac_addr[i]); } - } - else if (get_hostname_identifier(&result)) - { - /* Hooray */ - } - else - { - /* Fallback */ + } else if (get_hostname_identifier(&result)) { + // Hooray + } else { + // Fallback result.assign(L"nohost"); } return result; } -class universal_notifier_shmem_poller_t : public universal_notifier_t -{ - /* This is what our shared memory looks like. Everything here is stored in network byte order (big-endian) */ - struct universal_notifier_shmem_t - { +class universal_notifier_shmem_poller_t : public universal_notifier_t { + // This is what our shared memory looks like. Everything here is stored in network byte order + // (big-endian). + struct universal_notifier_shmem_t { uint32_t magic; uint32_t version; uint32_t universal_variable_seed; }; - + #define SHMEM_MAGIC_NUMBER 0xF154 #define SHMEM_VERSION_CURRENT 1000 - private: + private: long long last_change_time; uint32_t last_seed; volatile universal_notifier_shmem_t *region; - - void open_shmem() - { + + void open_shmem() { assert(region == NULL); - - // Use a path based on our uid to avoid collisions + + // Use a path based on our uid to avoid collisions. char path[NAME_MAX]; - snprintf(path, sizeof path, "/%ls_shmem_%d", program_name ? program_name : L"fish", getuid()); - + snprintf(path, sizeof path, "/%ls_shmem_%d", program_name ? program_name : L"fish", + getuid()); + bool errored = false; int fd = shm_open(path, O_RDWR | O_CREAT, 0600); - if (fd < 0) - { + if (fd < 0) { int err = errno; report_error(err, L"Unable to open shared memory with path '%s'", path); errored = true; } - /* Get the size */ + // Get the size. size_t size = 0; - if (! errored) - { + if (!errored) { struct stat buf = {}; - if (fstat(fd, &buf) < 0) - { + if (fstat(fd, &buf) < 0) { int err = errno; report_error(err, L"Unable to fstat shared memory object with path '%s'", path); errored = true; } size = buf.st_size; } - - /* Set the size, if it's too small */ - if (! errored && size < sizeof(universal_notifier_shmem_t)) - { - if (ftruncate(fd, sizeof(universal_notifier_shmem_t)) < 0) - { + + // Set the size, if it's too small. + if (!errored && size < sizeof(universal_notifier_shmem_t)) { + if (ftruncate(fd, sizeof(universal_notifier_shmem_t)) < 0) { int err = errno; report_error(err, L"Unable to truncate shared memory object with path '%s'", path); errored = true; } } - - /* Memory map the region */ - if (! errored) - { - void *addr = mmap(NULL, sizeof(universal_notifier_shmem_t), PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, 0); - if (addr == MAP_FAILED) - { + + // Memory map the region. + if (!errored) { + void *addr = mmap(NULL, sizeof(universal_notifier_shmem_t), PROT_READ | PROT_WRITE, + MAP_FILE | MAP_SHARED, fd, 0); + if (addr == MAP_FAILED) { int err = errno; - report_error(err, L"Unable to memory map shared memory object with path '%s'", path); + report_error(err, L"Unable to memory map shared memory object with path '%s'", + path); this->region = NULL; - } - else - { - this->region = static_cast(addr); + } else { + this->region = static_cast(addr); } } - - /* Close the fd, even if the mapping succeeded */ - if (fd >= 0) - { + + // Close the fd, even if the mapping succeeded. + if (fd >= 0) { close(fd); } - - /* Read the current seed */ + + // Read the current seed. this->poll(); // cppcheck-suppress memleak // addr not really leaked } - - public: - - /* Our notification involves changing the value in our shared memory. In practice, all clients will be in separate processes, so it suffices to set the value to a pid. For testing purposes, however, it's useful to keep them in the same process, so we increment the value. This isn't "safe" in the sense that multiple simultaneous increments may result in one being lost, but it should always result in the value being changed, which is sufficient. */ - void post_notification() - { - if (region != NULL) - { + + public: + // Our notification involves changing the value in our shared memory. In practice, all clients + // will be in separate processes, so it suffices to set the value to a pid. For testing + // purposes, however, it's useful to keep them in the same process, so we increment the value. + // This isn't "safe" in the sense that multiple simultaneous increments may result in one being + // lost, but it should always result in the value being changed, which is sufficient. + void post_notification() { + if (region != NULL) { /* Read off the seed */ uint32_t seed = ntohl(region->universal_variable_seed); - - /* Increment it. Don't let it wrap to zero. */ - do - { + + // Increment it. Don't let it wrap to zero. + do { seed++; - } - while (seed == 0); + } while (seed == 0); last_seed = seed; - - /* Write out our data */ + + // Write out our data. region->magic = htonl(SHMEM_MAGIC_NUMBER); region->version = htonl(SHMEM_VERSION_CURRENT); region->universal_variable_seed = htonl(seed); } } - - universal_notifier_shmem_poller_t() : last_change_time(0), last_seed(0), region(NULL) - { + + universal_notifier_shmem_poller_t() : last_change_time(0), last_seed(0), region(NULL) { open_shmem(); } - - ~universal_notifier_shmem_poller_t() - { - if (region != NULL) - { + + ~universal_notifier_shmem_poller_t() { + if (region != NULL) { // Behold: C++ in all its glory! void *address = const_cast(static_cast(region)); - if (munmap(address, sizeof(universal_notifier_shmem_t)) < 0) - { + if (munmap(address, sizeof(universal_notifier_shmem_t)) < 0) { wperror(L"munmap"); } } } - - bool poll() - { + + bool poll() { bool result = false; - if (region != NULL) - { + if (region != NULL) { uint32_t seed = ntohl(region->universal_variable_seed); - if (seed != last_seed) - { + if (seed != last_seed) { result = true; last_seed = seed; last_change_time = get_time(); @@ -1286,106 +1113,96 @@ class universal_notifier_shmem_poller_t : public universal_notifier_t } return result; } - - unsigned long usec_delay_between_polls() const - { - // If it's been less than five seconds since the last change, we poll quickly - // Otherwise we poll more slowly - // Note that a poll is a very cheap shmem read. The bad part about making this high - // is the process scheduling/wakeups it produces + + unsigned long usec_delay_between_polls() const { + // If it's been less than five seconds since the last change, we poll quickly Otherwise we + // poll more slowly. Note that a poll is a very cheap shmem read. The bad part about making + // this high is the process scheduling/wakeups it produces. unsigned long usec_per_sec = 1000000; - if (get_time() - last_change_time < 5LL * usec_per_sec) - { - return usec_per_sec / 10; //10 times a second - } - else - { - return usec_per_sec / 3; //3 times a second + if (get_time() - last_change_time < 5LL * usec_per_sec) { + return usec_per_sec / 10; // 10 times a second + } else { + return usec_per_sec / 3; // 3 times a second } } }; -/* A notifyd-based notifier. Very straightforward. */ -class universal_notifier_notifyd_t : public universal_notifier_t -{ +/// A notifyd-based notifier. Very straightforward. +class universal_notifier_notifyd_t : public universal_notifier_t { int notify_fd; int token; std::string name; - - void setup_notifyd() - { + + void setup_notifyd() { #if FISH_NOTIFYD_AVAILABLE - // per notify(3), the user.uid.%d style is only accessible to processes with that uid + // Per notify(3), the user.uid.%d style is only accessible to processes with that uid. char local_name[256]; - snprintf(local_name, sizeof local_name, "user.uid.%d.%ls.uvars", getuid(), program_name ? program_name : L"fish"); + snprintf(local_name, sizeof local_name, "user.uid.%d.%ls.uvars", getuid(), + program_name ? program_name : L"fish"); name.assign(local_name); - - uint32_t status = notify_register_file_descriptor(name.c_str(), &this->notify_fd, 0, &this->token); - if (status != NOTIFY_STATUS_OK) - { - fprintf(stderr, "Warning: notify_register_file_descriptor() failed with status %u. Universal variable notifications may not be received.", status); + + uint32_t status = + notify_register_file_descriptor(name.c_str(), &this->notify_fd, 0, &this->token); + if (status != NOTIFY_STATUS_OK) { + fprintf(stderr, + "Warning: notify_register_file_descriptor() failed with status %u. Universal " + "variable notifications may not be received.", + status); } - if (this->notify_fd >= 0) - { - // Mark us for non-blocking reads, and CLO_EXEC + if (this->notify_fd >= 0) { + // Mark us for non-blocking reads, and CLO_EXEC. int flags = fcntl(this->notify_fd, F_GETFL, 0); - if (flags >= 0 && ! (flags & O_NONBLOCK)) - { + if (flags >= 0 && !(flags & O_NONBLOCK)) { fcntl(this->notify_fd, F_SETFL, flags | O_NONBLOCK); } - + set_cloexec(this->notify_fd); - // Serious hack: notify_fd is likely the read end of a pipe. The other end is owned by libnotify, which does not mark it as CLO_EXEC (it should!) - // The next fd is probably notify_fd + 1 - // Do it ourselves. If the implementation changes and some other FD gets marked as CLO_EXEC, that's probably a good thing. + // Serious hack: notify_fd is likely the read end of a pipe. The other end is owned by + // libnotify, which does not mark it as CLO_EXEC (it should!). The next fd is probably + // notify_fd + 1. Do it ourselves. If the implementation changes and some other FD gets + // marked as CLO_EXEC, that's probably a good thing. set_cloexec(this->notify_fd + 1); } #endif } - -public: - universal_notifier_notifyd_t() : notify_fd(-1), token(-1 /* NOTIFY_TOKEN_INVALID */) - { + + public: + universal_notifier_notifyd_t() : notify_fd(-1), token(-1 /* NOTIFY_TOKEN_INVALID */) { setup_notifyd(); } - - ~universal_notifier_notifyd_t() - { - if (token != -1 /* NOTIFY_TOKEN_INVALID */) - { + + ~universal_notifier_notifyd_t() { + if (token != -1 /* NOTIFY_TOKEN_INVALID */) { #if FISH_NOTIFYD_AVAILABLE notify_cancel(token); #endif } } - - int notification_fd() - { - return notify_fd; - } - - bool notification_fd_became_readable(int fd) - { - /* notifyd notifications come in as 32 bit values. We don't care about the value. We set ourselves as non-blocking, so just read until we can't read any more. */ + + int notification_fd() { return notify_fd; } + + bool notification_fd_became_readable(int fd) { + // notifyd notifications come in as 32 bit values. We don't care about the value. We set + // ourselves as non-blocking, so just read until we can't read any more. assert(fd == notify_fd); bool read_something = false; unsigned char buff[64]; ssize_t amt_read; - do - { + do { amt_read = read(notify_fd, buff, sizeof buff); read_something = (read_something || amt_read > 0); } while (amt_read == sizeof buff); return read_something; } - - void post_notification() - { + + void post_notification() { #if FISH_NOTIFYD_AVAILABLE uint32_t status = notify_post(name.c_str()); - if (status != NOTIFY_STATUS_OK) - { - fprintf(stderr, "Warning: notify_post() failed with status %u. Universal variable notifications may not be sent.", status); + if (status != NOTIFY_STATUS_OK) { + fprintf(stderr, + "Warning: notify_post() failed with status %u. Universal variable " + "notifications may not be sent.", + status); } #endif } @@ -1394,180 +1211,158 @@ public: #define NAMED_PIPE_FLASH_DURATION_USEC (1000000 / 10) #define SUSTAINED_READABILITY_CLEANUP_DURATION_USEC (1000000 * 5) -/* Named-pipe based notifier. All clients open the same named pipe for reading and writing. The pipe's readability status is a trigger to enter polling mode. - - To post a notification, write some data to the pipe, wait a little while, and then read it back. - - To receive a notification, watch for the pipe to become readable. When it does, enter a polling mode until the pipe is no longer readable. To guard against the possibility of a shell exiting when there is data remaining in the pipe, if the pipe is kept readable too long, clients will attempt to read data out of it (to render it no longer readable). -*/ -class universal_notifier_named_pipe_t : public universal_notifier_t -{ +// Named-pipe based notifier. All clients open the same named pipe for reading and writing. The +// pipe's readability status is a trigger to enter polling mode. +// +// To post a notification, write some data to the pipe, wait a little while, and then read it back. +// +// To receive a notification, watch for the pipe to become readable. When it does, enter a polling +// mode until the pipe is no longer readable. To guard against the possibility of a shell exiting +// when there is data remaining in the pipe, if the pipe is kept readable too long, clients will +// attempt to read data out of it (to render it no longer readable). +class universal_notifier_named_pipe_t : public universal_notifier_t { int pipe_fd; long long readback_time_usec; size_t readback_amount; - + bool polling_due_to_readable_fd; long long drain_if_still_readable_time_usec; - - void make_pipe(const wchar_t *test_path) - { + + void make_pipe(const wchar_t *test_path) { wcstring vars_path = test_path ? wcstring(test_path) : default_named_pipe_path(); vars_path.append(L".notifier"); const std::string narrow_path = wcs2string(vars_path); - + int fd = wopen_cloexec(vars_path, O_RDWR | O_NONBLOCK, 0600); - if (fd < 0 && errno == ENOENT) - { - /* File doesn't exist, try creating it */ - if (mkfifo(narrow_path.c_str(), 0600) >= 0) - { + if (fd < 0 && errno == ENOENT) { + // File doesn't exist, try creating it. + if (mkfifo(narrow_path.c_str(), 0600) >= 0) { fd = wopen_cloexec(vars_path, O_RDWR | O_NONBLOCK, 0600); } } - if (fd < 0) - { - // Maybe open failed, maybe mkfifo failed + if (fd < 0) { + // Maybe open failed, maybe mkfifo failed. int err = errno; - // We explicitly do NOT report an error for ENOENT or EACCESS - // This works around #1955, where $XDG_RUNTIME_DIR may get a bogus value under suc - if (err != ENOENT && err != EPERM) - { - report_error(err, L"Unable to make or open a FIFO for universal variables with path '%ls'", vars_path.c_str()); + // We explicitly do NOT report an error for ENOENT or EACCESS. This works around #1955, + // where $XDG_RUNTIME_DIR may get a bogus value under success. + if (err != ENOENT && err != EPERM) { + report_error( + err, L"Unable to make or open a FIFO for universal variables with path '%ls'", + vars_path.c_str()); } - pipe_fd= -1; - } - else - { + pipe_fd = -1; + } else { pipe_fd = fd; } } - - void drain_excessive_data() - { - // The pipe seems to have data on it, that won't go away - // Read a big chunk out of it. - // We don't read until it's exhausted, because if someone were to pipe say /dev/null, that would cause us to hang! + + void drain_excessive_data() { + // The pipe seems to have data on it, that won't go away. Read a big chunk out of it. We + // don't read until it's exhausted, because if someone were to pipe say /dev/null, that + // would cause us to hang! size_t read_amt = 64 * 1024; void *buff = malloc(read_amt); read_ignore(this->pipe_fd, buff, read_amt); free(buff); } - - public: - explicit universal_notifier_named_pipe_t(const wchar_t *test_path) : pipe_fd(-1), readback_time_usec(0), readback_amount(0), polling_due_to_readable_fd(false), drain_if_still_readable_time_usec(0) - { + + public: + explicit universal_notifier_named_pipe_t(const wchar_t *test_path) + : pipe_fd(-1), + readback_time_usec(0), + readback_amount(0), + polling_due_to_readable_fd(false), + drain_if_still_readable_time_usec(0) { make_pipe(test_path); } - - ~universal_notifier_named_pipe_t() - { - if (pipe_fd >= 0) - { + + ~universal_notifier_named_pipe_t() { + if (pipe_fd >= 0) { close(pipe_fd); } } - - int notification_fd() - { - if (polling_due_to_readable_fd) - { - // We are in polling mode because we think our fd is readable - // This means that, if we return it to be select()'d on, we'll be called back immediately - // So don't return it + + int notification_fd() { + if (polling_due_to_readable_fd) { + // We are in polling mode because we think our fd is readable. This means that, if we + // return it to be select()'d on, we'll be called back immediately. So don't return it. return -1; - } - else - { - // We are not in polling mode - // Return the fd so it can be watched + } else { + // We are not in polling mode. Return the fd so it can be watched. return pipe_fd; } } - - bool notification_fd_became_readable(int fd) - { - // Our fd is readable. We deliberately do not read anything out of it: if we did, other sessions may miss the notification. - // Instead, we go into "polling mode:" we do not select() on our fd for a while, and sync periodically until the fd is no longer readable. - // However, if we are the one who posted the notification, we don't sync (until we clean up!) + + bool notification_fd_became_readable(int fd) { + // Our fd is readable. We deliberately do not read anything out of it: if we did, other + // sessions may miss the notification. Instead, we go into "polling mode:" we do not + // select() on our fd for a while, and sync periodically until the fd is no longer readable. + // However, if we are the one who posted the notification, we don't sync (until we clean + // up!) bool should_sync = false; - if (readback_time_usec == 0) - { + if (readback_time_usec == 0) { polling_due_to_readable_fd = true; - drain_if_still_readable_time_usec = get_time() + SUSTAINED_READABILITY_CLEANUP_DURATION_USEC; + drain_if_still_readable_time_usec = + get_time() + SUSTAINED_READABILITY_CLEANUP_DURATION_USEC; should_sync = true; } return should_sync; } - - void post_notification() - { - if (pipe_fd >= 0) - { - // We need to write some data (any data) to the pipe, then wait for a while, then read it back. - // Nobody is expected to read it except us. + + void post_notification() { + if (pipe_fd >= 0) { + // We need to write some data (any data) to the pipe, then wait for a while, then read + // it back. Nobody is expected to read it except us. int pid_nbo = htonl(getpid()); ssize_t amt_written = write(this->pipe_fd, &pid_nbo, sizeof pid_nbo); - if (amt_written < 0) - { - if (errno == EWOULDBLOCK || errno == EAGAIN) - { + if (amt_written < 0) { + if (errno == EWOULDBLOCK || errno == EAGAIN) { // Very unsual: the pipe is full! drain_excessive_data(); } } - - // Now schedule a read for some time in the future + + // Now schedule a read for some time in the future. this->readback_time_usec = get_time() + NAMED_PIPE_FLASH_DURATION_USEC; this->readback_amount += sizeof pid_nbo; } } - - unsigned long usec_delay_between_polls() const - { + + unsigned long usec_delay_between_polls() const { unsigned long readback_delay = ULONG_MAX; - if (this->readback_time_usec > 0) - { + if (this->readback_time_usec > 0) { // How long until the readback? long long now = get_time(); - if (now >= this->readback_time_usec) - { + if (now >= this->readback_time_usec) { // Oops, it already passed! Return something tiny. readback_delay = 1000; - } - else - { + } else { readback_delay = (unsigned long)(this->readback_time_usec - now); } } - + unsigned long polling_delay = ULONG_MAX; - if (polling_due_to_readable_fd) - { - // We're in polling mode - // Don't return a value less than our polling interval + if (polling_due_to_readable_fd) { + // We're in polling mode. Don't return a value less than our polling interval. polling_delay = NAMED_PIPE_FLASH_DURATION_USEC; } - - // Now return the smaller of the two values - // If we get ULONG_MAX, it means there's no more need to poll; in that case return 0 + + // Now return the smaller of the two values. If we get ULONG_MAX, it means there's no more + // need to poll; in that case return 0. unsigned long result = mini(readback_delay, polling_delay); - if (result == ULLONG_MAX) - { + if (result == ULLONG_MAX) { result = 0; } return result; } - - bool poll() - { + + bool poll() { bool result = false; - - // Check if we are past the readback time - if (this->readback_time_usec > 0 && get_time() >= this->readback_time_usec) - { + + // Check if we are past the readback time. + if (this->readback_time_usec > 0 && get_time() >= this->readback_time_usec) { // Read back what we wrote. We do nothing with the value. - while (this->readback_amount > 0) - { + while (this->readback_amount > 0) { char buff[64]; size_t amt_to_read = mini(this->readback_amount, sizeof buff); read_ignore(this->pipe_fd, buff, amt_to_read); @@ -1576,79 +1371,63 @@ class universal_notifier_named_pipe_t : public universal_notifier_t assert(this->readback_amount == 0); this->readback_time_usec = 0; } - - // Check to see if we are doing readability polling - if (polling_due_to_readable_fd && pipe_fd >= 0) - { - // We are polling, so we are definitely going to sync + + // Check to see if we are doing readability polling. + if (polling_due_to_readable_fd && pipe_fd >= 0) { + // We are polling, so we are definitely going to sync. result = true; - - // See if this is still readable + + // See if this is still readable. fd_set fds; FD_ZERO(&fds); FD_SET(this->pipe_fd, &fds); struct timeval timeout = {}; select(this->pipe_fd + 1, &fds, NULL, NULL, &timeout); - if (! FD_ISSET(this->pipe_fd, &fds)) - { - // No longer readable, no longer polling + if (!FD_ISSET(this->pipe_fd, &fds)) { + // No longer readable, no longer polling. polling_due_to_readable_fd = false; drain_if_still_readable_time_usec = 0; - } - else - { - // Still readable. If it's been readable for a long time, there is probably lingering data on the pipe - if (get_time() >= drain_if_still_readable_time_usec) - { + } else { + // Still readable. If it's been readable for a long time, there is probably + // lingering data on the pipe. + if (get_time() >= drain_if_still_readable_time_usec) { drain_excessive_data(); } } } - + return result; } }; -class universal_notifier_null_t : public universal_notifier_t -{ - /* Does nothing! */ -}; +class universal_notifier_null_t : public universal_notifier_t {}; // does nothing -static universal_notifier_t::notifier_strategy_t fetch_default_strategy_from_environment() -{ +static universal_notifier_t::notifier_strategy_t fetch_default_strategy_from_environment() { universal_notifier_t::notifier_strategy_t result = universal_notifier_t::strategy_default; - const struct - { + const struct { const char *name; universal_notifier_t::notifier_strategy_t strat; - } options[] = - { - {"default", universal_notifier_t::strategy_default}, - {"shmem", universal_notifier_t::strategy_shmem_polling}, - {"pipe", universal_notifier_t::strategy_named_pipe}, - {"notifyd", universal_notifier_t::strategy_notifyd} - }; + } options[] = {{"default", universal_notifier_t::strategy_default}, + {"shmem", universal_notifier_t::strategy_shmem_polling}, + {"pipe", universal_notifier_t::strategy_named_pipe}, + {"notifyd", universal_notifier_t::strategy_notifyd}}; const size_t opt_count = sizeof options / sizeof *options; const char *var = getenv(UNIVERSAL_NOTIFIER_ENV_NAME); - if (var != NULL && var[0] != '\0') - { + if (var != NULL && var[0] != '\0') { size_t i; - for (i=0; i < opt_count; i++) - { - if (! strcmp(var, options[i].name)) - { + for (i = 0; i < opt_count; i++) { + if (!strcmp(var, options[i].name)) { result = options[i].strat; break; } } - if (i >= opt_count) - { - fprintf(stderr, "Warning: unrecognized value for %s: '%s'\n", UNIVERSAL_NOTIFIER_ENV_NAME, var); + if (i >= opt_count) { + fprintf(stderr, "Warning: unrecognized value for %s: '%s'\n", + UNIVERSAL_NOTIFIER_ENV_NAME, var); fprintf(stderr, "Warning: valid values are "); - for (size_t j=0; j < opt_count; j++) - { + for (size_t j = 0; j < opt_count; j++) { fprintf(stderr, "%s%s", j > 0 ? ", " : "", options[j].name); } fputc('\n', stderr); @@ -1657,11 +1436,10 @@ static universal_notifier_t::notifier_strategy_t fetch_default_strategy_from_env return result; } -universal_notifier_t::notifier_strategy_t universal_notifier_t::resolve_default_strategy() -{ - static universal_notifier_t::notifier_strategy_t s_explicit_strategy = fetch_default_strategy_from_environment(); - if (s_explicit_strategy != strategy_default) - { +universal_notifier_t::notifier_strategy_t universal_notifier_t::resolve_default_strategy() { + static universal_notifier_t::notifier_strategy_t s_explicit_strategy = + fetch_default_strategy_from_environment(); + if (s_explicit_strategy != strategy_default) { return s_explicit_strategy; } #if FISH_NOTIFYD_AVAILABLE @@ -1673,67 +1451,47 @@ universal_notifier_t::notifier_strategy_t universal_notifier_t::resolve_default_ #endif } -universal_notifier_t &universal_notifier_t::default_notifier() -{ +universal_notifier_t &universal_notifier_t::default_notifier() { static universal_notifier_t *result = new_notifier_for_strategy(strategy_default); return *result; } -universal_notifier_t *universal_notifier_t::new_notifier_for_strategy(universal_notifier_t::notifier_strategy_t strat, const wchar_t *test_path) -{ - if (strat == strategy_default) - { +universal_notifier_t *universal_notifier_t::new_notifier_for_strategy( + universal_notifier_t::notifier_strategy_t strat, const wchar_t *test_path) { + if (strat == strategy_default) { strat = resolve_default_strategy(); } - switch (strat) - { - case strategy_shmem_polling: + switch (strat) { + case strategy_shmem_polling: { return new universal_notifier_shmem_poller_t(); - - case strategy_notifyd: + } + case strategy_notifyd: { return new universal_notifier_notifyd_t(); - - case strategy_named_pipe: + } + case strategy_named_pipe: { return new universal_notifier_named_pipe_t(test_path); - - case strategy_null: + } + case strategy_null: { return new universal_notifier_null_t(); - - default: + } + default: { fprintf(stderr, "Unsupported strategy %d\n", strat); return NULL; + } } } -/* Default implementations. */ -universal_notifier_t::universal_notifier_t() -{ -} +// Default implementations. +universal_notifier_t::universal_notifier_t() {} -universal_notifier_t::~universal_notifier_t() -{ -} +universal_notifier_t::~universal_notifier_t() {} -int universal_notifier_t::notification_fd() -{ - return -1; -} +int universal_notifier_t::notification_fd() { return -1; } -void universal_notifier_t::post_notification() -{ -} +void universal_notifier_t::post_notification() {} -bool universal_notifier_t::poll() -{ - return false; -} +bool universal_notifier_t::poll() { return false; } -unsigned long universal_notifier_t::usec_delay_between_polls() const -{ - return 0; -} +unsigned long universal_notifier_t::usec_delay_between_polls() const { return 0; } -bool universal_notifier_t::notification_fd_became_readable(int fd) -{ - return false; -} +bool universal_notifier_t::notification_fd_became_readable(int fd) { return false; } diff --git a/src/env_universal_common.h b/src/env_universal_common.h index 8e319fe1c..658f3f412 100644 --- a/src/env_universal_common.h +++ b/src/env_universal_common.h @@ -2,174 +2,173 @@ #define FISH_ENV_UNIVERSAL_COMMON_H #include -#include #include -#include +#include #include +#include #include #include "common.h" -#include "wutil.h" #include "env.h" +#include "wutil.h" -/** - The different types of messages found in the fishd file -*/ -typedef enum -{ - SET, - SET_EXPORT, - ERASE -} fish_message_type_t; +/// The different types of messages found in the fishd file. +typedef enum { SET, SET_EXPORT, ERASE } fish_message_type_t; -/** - Callback data, reflecting a change in universal variables -*/ -struct callback_data_t -{ +/// Callback data, reflecting a change in universal variables. +struct callback_data_t { fish_message_type_t type; wcstring key; wcstring val; - - callback_data_t(fish_message_type_t t, const wcstring &k, const wcstring &v) : type(t), key(k), val(v) - { - } + + callback_data_t(fish_message_type_t t, const wcstring &k, const wcstring &v) + : type(t), key(k), val(v) {} }; typedef std::vector callback_data_list_t; -/** Class representing universal variables */ -class env_universal_t -{ - /* Current values */ - var_table_t vars; - - /* Keys that have been modified, and need to be written. A value here that is not present in vars indicates a deleted value. */ +/// Class representing universal variables. +class env_universal_t { + var_table_t vars; // current values + + // Keys that have been modified, and need to be written. A value here that is not present in + // vars indicates a deleted value. std::set modified; - - /* Path that we save to. If empty, use the default */ + + // Path that we save to. If empty, use the default. const wcstring explicit_vars_path; - + mutable pthread_mutex_t lock; bool tried_renaming; bool load_from_path(const wcstring &path, callback_data_list_t *callbacks); void load_from_fd(int fd, callback_data_list_t *callbacks); - + void set_internal(const wcstring &key, const wcstring &val, bool exportv, bool overwrite); bool remove_internal(const wcstring &name); - - /* Functions concerned with saving */ + + // Functions concerned with saving. bool open_and_acquire_lock(const wcstring &path, int *out_fd); bool open_temporary_file(const wcstring &directory, wcstring *out_path, int *out_fd); bool write_to_fd(int fd, const wcstring &path); bool move_new_vars_file_into_place(const wcstring &src, const wcstring &dst); - - /* File id from which we last read */ + + // File id from which we last read. file_id_t last_read_file; - - /* Given a variable table, generate callbacks representing the difference between our vars and the new vars */ + + // Given a variable table, generate callbacks representing the difference between our vars and + // the new vars. void generate_callbacks(const var_table_t &new_vars, callback_data_list_t *callbacks) const; - - /* Given a variable table, copy unmodified values into self. May destructively modified vars_to_acquire. */ + + // Given a variable table, copy unmodified values into self. May destructively modified + // vars_to_acquire. void acquire_variables(var_table_t *vars_to_acquire); - + static void parse_message_internal(const wcstring &msg, var_table_t *vars, wcstring *storage); static var_table_t read_message_internal(int fd); - -public: + + public: explicit env_universal_t(const wcstring &path); ~env_universal_t(); - - /* Get the value of the variable with the specified name */ + + // Get the value of the variable with the specified name. env_var_t get(const wcstring &name) const; - - /* Returns whether the variable with the given name is exported, or false if it does not exist */ + + // Returns whether the variable with the given name is exported, or false if it does not exist. bool get_export(const wcstring &name) const; - - /* Sets a variable */ + + // Sets a variable. void set(const wcstring &key, const wcstring &val, bool exportv); - - /* Removes a variable. Returns true if it was found, false if not. */ + + // Removes a variable. Returns true if it was found, false if not. bool remove(const wcstring &name); - - /* Gets variable names */ + + // Gets variable names. wcstring_list_t get_names(bool show_exported, bool show_unexported) const; - - /** Loads variables at the correct path */ + + /// Loads variables at the correct path. bool load(); - /** Reads and writes variables at the correct path. Returns true if modified variables were written. */ + /// Reads and writes variables at the correct path. Returns true if modified variables were + /// written. bool sync(callback_data_list_t *callbacks); }; -/** The "universal notifier" is an object responsible for broadcasting and receiving universal variable change notifications. These notifications do not contain the change, but merely indicate that the uvar file has changed. It is up to the uvar subsystem to re-read the file. - - We support a few notificatins strategies. Not all strategies are supported on all platforms. - - Notifiers may request polling, and/or provide a file descriptor to be watched for readability in select(). - - To request polling, the notifier overrides usec_delay_between_polls() to return a positive value. That value will be used as the timeout in select(). When select returns, the loop invokes poll(). poll() should return true to indicate that the file may have changed. - - To provide a file descriptor, the notifier overrides notification_fd() to return a non-negative fd. This will be added to the "read" file descriptor list in select(). If the fd is readable, notification_fd_became_readable() will be called; that function should be overridden to return true if the file may have changed. - -*/ -class universal_notifier_t -{ -public: - enum notifier_strategy_t - { - // Default meta-strategy to use the 'best' notifier for the system +/// The "universal notifier" is an object responsible for broadcasting and receiving universal +/// variable change notifications. These notifications do not contain the change, but merely +/// indicate that the uvar file has changed. It is up to the uvar subsystem to re-read the file. +/// +/// We support a few notificatins strategies. Not all strategies are supported on all platforms. +/// +/// Notifiers may request polling, and/or provide a file descriptor to be watched for readability in +/// select(). +/// +/// To request polling, the notifier overrides usec_delay_between_polls() to return a positive +/// value. That value will be used as the timeout in select(). When select returns, the loop invokes +/// poll(). poll() should return true to indicate that the file may have changed. +/// +/// To provide a file descriptor, the notifier overrides notification_fd() to return a non-negative +/// fd. This will be added to the "read" file descriptor list in select(). If the fd is readable, +/// notification_fd_became_readable() will be called; that function should be overridden to return +/// true if the file may have changed. +class universal_notifier_t { + public: + enum notifier_strategy_t { + // Default meta-strategy to use the 'best' notifier for the system. strategy_default, - - // Use a value in shared memory. Simple, but requires polling and therefore semi-frequent wakeups. + + // Use a value in shared memory. Simple, but requires polling and therefore semi-frequent + // wakeups. strategy_shmem_polling, - - // Strategy that uses a named pipe. Somewhat complex, but portable and doesn't require polling most of the time. + + // Strategy that uses a named pipe. Somewhat complex, but portable and doesn't require + // polling most of the time. strategy_named_pipe, - + // Strategy that uses notify(3). Simple and efficient, but OS X only. strategy_notifyd, - - // Null notifier, does nothing + + // Null notifier, does nothing. strategy_null }; - protected: + protected: universal_notifier_t(); - - private: - /* No copying */ + + private: + // No copying. universal_notifier_t &operator=(const universal_notifier_t &); universal_notifier_t(const universal_notifier_t &x); static notifier_strategy_t resolve_default_strategy(); - - public: - + + public: virtual ~universal_notifier_t(); - - /* Factory constructor. Free with delete */ - static universal_notifier_t *new_notifier_for_strategy(notifier_strategy_t strat, const wchar_t *test_path = NULL); - - /* Default instance. Other instances are possible for testing. */ + + // Factory constructor. Free with delete. + static universal_notifier_t *new_notifier_for_strategy(notifier_strategy_t strat, + const wchar_t *test_path = NULL); + + // Default instance. Other instances are possible for testing. static universal_notifier_t &default_notifier(); - - /* Does a fast poll(). Returns true if changed. */ + + // Does a fast poll(). Returns true if changed. virtual bool poll(); - - /* Triggers a notification */ + + // Triggers a notification. virtual void post_notification(); - - /* Recommended delay between polls. A value of 0 means no polling required (so no timeout) */ + + // Recommended delay between polls. A value of 0 means no polling required (so no timeout). virtual unsigned long usec_delay_between_polls() const; - - /* Returns the fd from which to watch for events, or -1 if none */ + + // Returns the fd from which to watch for events, or -1 if none. virtual int notification_fd(); - - /* The notification_fd is readable; drain it. Returns true if a notification is considered to have been posted. */ + + // The notification_fd is readable; drain it. Returns true if a notification is considered to + // have been posted. virtual bool notification_fd_became_readable(int fd); }; -/* Environment variable for requesting a particular universal notifier. See fetch_default_strategy_from_environment for names. */ +// Environment variable for requesting a particular universal notifier. See +// fetch_default_strategy_from_environment for names. #define UNIVERSAL_NOTIFIER_ENV_NAME "fish_universal_notifier" #endif From 32c241f51be281043aa1e30ca3fb9b277cd28d6b Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Thu, 28 Apr 2016 19:19:50 -0700 Subject: [PATCH 156/363] retyle event module to match project style Reduces lint errors from 39 to 30 (-23%). Line count from 915 to 670 (-27%). Another step in resolving issue #2902. --- src/event.cpp | 670 +++++++++++++++++--------------------------------- src/event.h | 205 +++++++-------- 2 files changed, 316 insertions(+), 559 deletions(-) diff --git a/src/event.cpp b/src/event.cpp index ee1d7c0a9..26579101f 100644 --- a/src/event.cpp +++ b/src/event.cpp @@ -1,226 +1,164 @@ -/** \file event.c - - Functions for handling event triggers -*/ -#include +// Functions for handling event triggers. #include -#include -#include -#include -#include +#include #include +#include +#include +#include +#include -#include "fallback.h" // IWYU pragma: keep -#include "wutil.h" // IWYU pragma: keep -#include "input_common.h" -#include "proc.h" -#include "parser.h" #include "common.h" #include "event.h" -#include "signal.h" +#include "fallback.h" // IWYU pragma: keep +#include "input_common.h" #include "io.h" +#include "parser.h" +#include "proc.h" +#include "signal.h" +#include "wutil.h" // IWYU pragma: keep -/** - Number of signals that can be queued before an overflow occurs -*/ +/// Number of signals that can be queued before an overflow occurs. #define SIG_UNHANDLED_MAX 64 -/** - This struct contains a list of generated signals waiting to be - dispatched -*/ -typedef struct -{ - /** - Number of delivered signals - */ +/// This struct contains a list of generated signals waiting to be dispatched. +typedef struct { + /// Number of delivered signals. volatile int count; - /** - Whether signals have been skipped - */ + /// Whether signals have been skipped. volatile int overflow; - /** - Array of signal events - */ + /// Array of signal events. volatile 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[2]= {{},{}}; +/// The signal event list. Actually two separate lists. One which is active, which is the one that +/// new events is written to. The inactive one contains the events that are currently beeing +/// performed. +static signal_list_t sig_list[2] = {{}, {}}; -/** - The index of sig_list that is the list of signals currently written to -*/ -static volatile int active_list=0; +/// The index of sig_list that is the list of signals currently written to. +static volatile int active_list = 0; typedef std::vector event_list_t; -/** - List of event handlers. -*/ +/// List of event handlers. static event_list_t s_event_handlers; -/** - List of event handlers that should be removed -*/ +/// List of event handlers that should be removed. static event_list_t killme; -/** - List of events that have been sent but have not yet been delivered because they are blocked. -*/ +/// List of events that have been sent but have not yet been delivered because they are blocked. static event_list_t blocked; -/** Variables (one per signal) set when a signal is observed. This is inspected by a signal handler. */ +/// Variables (one per signal) set when a signal is observed. This is inspected by a signal handler. static volatile bool s_observed_signals[NSIG] = {}; -static void set_signal_observed(int sig, bool val) -{ +static void set_signal_observed(int sig, bool val) { ASSERT_IS_MAIN_THREAD(); - if (sig >= 0 && (size_t)sig < sizeof s_observed_signals / sizeof *s_observed_signals) - { + if (sig >= 0 && (size_t)sig < sizeof s_observed_signals / sizeof *s_observed_signals) { s_observed_signals[sig] = val; } } -/** - Tests if one event instance matches the definition of a event - class. If both the class and the instance name a function, - they must name the same function. - -*/ -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) - { +/// Tests if one event instance matches the definition of a event class. If both the class and the +/// instance name a function, they must name the same function. +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) { 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) - { - - case EVENT_SIGNAL: - if (classv.param1.signal == EVENT_ANY_SIGNAL) - return 1; + switch (classv.type) { + case EVENT_SIGNAL: { + if (classv.param1.signal == EVENT_ANY_SIGNAL) return 1; return classv.param1.signal == instance.param1.signal; - - case EVENT_VARIABLE: + } + case EVENT_VARIABLE: { return instance.str_param1 == classv.str_param1; - - case EVENT_EXIT: - if (classv.param1.pid == EVENT_ANY_PID) - return 1; + } + case EVENT_EXIT: { + if (classv.param1.pid == EVENT_ANY_PID) return 1; return classv.param1.pid == instance.param1.pid; - - case EVENT_JOB_ID: + } + case EVENT_JOB_ID: { return classv.param1.job_id == instance.param1.job_id; - - case EVENT_GENERIC: + } + case EVENT_GENERIC: { return instance.str_param1 == classv.str_param1; - + } } - /** - This should never be reached - */ + // This should never be reached. debug(0, "Warning: Unreachable code reached in event_match in event.cpp\n"); return 0; } - - -/** - Test if specified event is blocked -*/ -static int event_is_blocked(const event_t &e) -{ +/// Test if specified event is blocked. +static int event_is_blocked(const event_t &e) { const block_t *block; parser_t &parser = parser_t::principal_parser(); size_t idx = 0; - while ((block = parser.block_at_index(idx++))) - { - if (event_block_list_blocks_type(block->event_blocks, e.type)) - return true; - + while ((block = parser.block_at_index(idx++))) { + 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) { wcstring result; - switch (e.type) - { - - case EVENT_SIGNAL: - result = format_string(_(L"signal handler for %ls (%ls)"), sig2wcs(e.param1.signal), signal_get_desc(e.param1.signal)); - break; - - case EVENT_VARIABLE: - result = format_string(_(L"handler for variable '%ls'"), e.str_param1.c_str()); - break; - - case EVENT_EXIT: - if (e.param1.pid > 0) - { - result = format_string(_(L"exit handler for process %d"), e.param1.pid); - } - else - { - job_t *j = job_get_from_pid(-e.param1.pid); - if (j) - result = format_string(_(L"exit handler for job %d, '%ls'"), j->job_id, j->command_wcstr()); - else - result = format_string(_(L"exit handler for job with process group %d"), -e.param1.pid); - } - - break; - - case EVENT_JOB_ID: - { - job_t *j = job_get(e.param1.job_id); - if (j) - result = format_string(_(L"exit handler for job %d, '%ls'"), j->job_id, j->command_wcstr()); - else - result = format_string(_(L"exit handler for job with job id %d"), e.param1.job_id); - + switch (e.type) { + case EVENT_SIGNAL: { + result = format_string(_(L"signal handler for %ls (%ls)"), sig2wcs(e.param1.signal), + signal_get_desc(e.param1.signal)); break; } - - case EVENT_GENERIC: + case EVENT_VARIABLE: { + result = format_string(_(L"handler for variable '%ls'"), e.str_param1.c_str()); + break; + } + case EVENT_EXIT: { + if (e.param1.pid > 0) { + result = format_string(_(L"exit handler for process %d"), e.param1.pid); + } else { + job_t *j = job_get_from_pid(-e.param1.pid); + if (j) + result = format_string(_(L"exit handler for job %d, '%ls'"), j->job_id, + j->command_wcstr()); + else + result = format_string(_(L"exit handler for job with process group %d"), + -e.param1.pid); + } + break; + } + case EVENT_JOB_ID: { + job_t *j = job_get(e.param1.job_id); + if (j) { + result = format_string(_(L"exit handler for job %d, '%ls'"), j->job_id, + j->command_wcstr()); + } else { + result = format_string(_(L"exit handler for job with job id %d"), e.param1.job_id); + } + break; + } + case EVENT_GENERIC: { result = format_string(_(L"handler for generic event '%ls'"), e.str_param1.c_str()); break; - - default: + } + default: { result = format_string(_(L"Unknown event type '0x%x'"), e.type); break; - + } } 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()); @@ -228,104 +166,84 @@ static void show_all_handlers(void) } #endif -/* - Give a more condensed description of \c event compared to \c event_get_desc. - It includes what function will fire if the \c event is an event handler. - */ -static wcstring event_desc_compact(const event_t &event) -{ +/// Give a more condensed description of \c event compared to \c event_get_desc. It includes what +/// function will fire if the \c event is an event handler. +static wcstring event_desc_compact(const event_t &event) { wcstring res; wchar_t const *temp; int sig; - switch (event.type) - { - case EVENT_ANY: + switch (event.type) { + case EVENT_ANY: { res = L"EVENT_ANY"; break; - case EVENT_VARIABLE: - if (event.str_param1.c_str()) - { + } + case EVENT_VARIABLE: { + if (event.str_param1.c_str()) { res = format_string(L"EVENT_VARIABLE($%ls)", event.str_param1.c_str()); - } - else - { + } else { res = L"EVENT_VARIABLE([any])"; } break; - case EVENT_SIGNAL: + } + case EVENT_SIGNAL: { sig = event.param1.signal; - if (sig == EVENT_ANY_SIGNAL) - { + if (sig == EVENT_ANY_SIGNAL) { temp = L"[all signals]"; - } - else if (sig == 0) - { + } else if (sig == 0) { temp = L"not set"; - } - else - { + } else { temp = sig2wcs(sig); } res = format_string(L"EVENT_SIGNAL(%ls)", temp); break; - case EVENT_EXIT: - if (event.param1.pid == EVENT_ANY_PID) - { + } + case EVENT_EXIT: { + if (event.param1.pid == EVENT_ANY_PID) { res = wcstring(L"EVENT_EXIT([all child processes])"); - } - else if (event.param1.pid > 0) - { + } else if (event.param1.pid > 0) { res = format_string(L"EVENT_EXIT(pid %d)", event.param1.pid); - } - else - { + } else { job_t *j = job_get_from_pid(-event.param1.pid); if (j) - res = format_string(L"EVENT_EXIT(jobid %d: \"%ls\")", j->job_id, j->command_wcstr()); + res = format_string(L"EVENT_EXIT(jobid %d: \"%ls\")", j->job_id, + j->command_wcstr()); else res = format_string(L"EVENT_EXIT(pgid %d)", -event.param1.pid); } break; - case EVENT_JOB_ID: - { + } + case EVENT_JOB_ID: { job_t *j = job_get(event.param1.job_id); if (j) - res = format_string(L"EVENT_JOB_ID(job %d: \"%ls\")", j->job_id, j->command_wcstr()); + res = + format_string(L"EVENT_JOB_ID(job %d: \"%ls\")", j->job_id, j->command_wcstr()); else res = format_string(L"EVENT_JOB_ID(jobid %d)", event.param1.job_id); break; } - case EVENT_GENERIC: + case EVENT_GENERIC: { res = format_string(L"EVENT_GENERIC(%ls)", event.str_param1.c_str()); break; - default: - res = format_string(L"unknown/illegal event(%x)", event.type); + } + default: { res = format_string(L"unknown/illegal event(%x)", event.type); } } - if (event.function_name.size()) - { + if (event.function_name.size()) { return format_string(L"%ls: \"%ls\"", res.c_str(), event.function_name.c_str()); - } - else - { + } else { return res; } } - -void event_add_handler(const event_t &event) -{ +void event_add_handler(const event_t &event) { event_t *e; - if (debug_level >= 3) - { + if (debug_level >= 3) { wcstring desc = event_desc_compact(event); debug(3, "register: %ls\n", desc.c_str()); } e = new event_t(event); - - if (e->type == EVENT_SIGNAL) - { + if (e->type == EVENT_SIGNAL) { signal_handle(e->param1.signal, 1); set_signal_observed(e->param1.signal, true); } @@ -333,106 +251,75 @@ void event_add_handler(const event_t &event) s_event_handlers.push_back(e); } -void event_remove(const event_t &criterion) -{ +void event_remove(const event_t &criterion) { event_list_t new_list; - if (debug_level >= 3) - { + if (debug_level >= 3) { wcstring desc = event_desc_compact(criterion); debug(3, "unregister: %ls\n", desc.c_str()); } - /* - 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 (s_event_handlers.empty()) return; - if (s_event_handlers.empty()) - return; - - for (size_t 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) - { + if (event_get(e, 0) == 1) { signal_handle(e.param1.signal, 0); set_signal_observed(e.param1.signal, 0); } } - } - else - { + } else { new_list.push_back(n); } } s_event_handlers.swap(new_list); } -int event_get(const event_t &criterion, std::vector *out) -{ +int event_get(const event_t &criterion, std::vector *out) { int found = 0; - for (size_t i=0; i < s_event_handlers.size(); i++) - { + for (size_t i = 0; i < s_event_handlers.size(); i++) { event_t *n = s_event_handlers.at(i); - if (event_match(criterion, *n)) - { + if (event_match(criterion, *n)) { found++; - if (out) - out->push_back(n); + if (out) out->push_back(n); } } return found; } -bool event_is_signal_observed(int sig) -{ - /* We are in a signal handler! Don't allocate memory, etc. - */ +bool event_is_signal_observed(int sig) { + // We are in a signal handler! Don't allocate memory, etc. bool result = false; - if (sig >= 0 && sig < sizeof s_observed_signals / sizeof *s_observed_signals) - { + if (sig >= 0 && sig < sizeof s_observed_signals / sizeof *s_observed_signals) { result = s_observed_signals[sig]; } return result; } -/** - Free all events in the kill list -*/ -static void event_free_kills() -{ +/// Free all events in the kill list. +static void event_free_kills() { for_each(killme.begin(), killme.end(), event_free); killme.resize(0); } -/** - Test if the specified event is waiting to be killed -*/ -static int event_is_killed(const event_t &e) -{ +/// Test if the specified event is waiting to be killed. +static int event_is_killed(const event_t &e) { return std::find(killme.begin(), killme.end(), &e) != killme.end(); } -/* Callback for firing (and then deleting) an event */ -static void fire_event_callback(void *arg) -{ +/// Callback for firing (and then deleting) an event. +static void fire_event_callback(void *arg) { ASSERT_IS_MAIN_THREAD(); assert(arg != NULL); event_t *event = static_cast(arg); @@ -440,83 +327,53 @@ static void fire_event_callback(void *arg) delete event; } -/** - Perform the specified event. Since almost all event firings will - not be matched by even a single event handler, we make sure to - optimize the 'no matches' path. This means that nothing is - allocated/initialized unless needed. -*/ -static void event_fire_internal(const event_t &event) -{ - +/// Perform the specified event. Since almost all event firings will not be matched by even a single +/// event handler, we make sure to optimize the 'no matches' path. This means that nothing is +/// allocated/initialized unless needed. +static void event_fire_internal(const event_t &event) { event_list_t fire; - /* - First we free all events that have been removed, but only if this - invocation of event_fire_internal is not a recursive call. - */ - if (is_event <= 1) - event_free_kills(); + // First we free all events that have been removed, but only if this invocation of + // event_fire_internal is not a recursive call. + if (is_event <= 1) event_free_kills(); - if (s_event_handlers.empty()) - return; + if (s_event_handlers.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 (size_t i=0; ifunction_name; - for (size_t j=0; j < event.arguments.size(); j++) - { + for (size_t j = 0; j < event.arguments.size(); j++) { wcstring arg_esc = escape_string(event.arguments.at(j), 1); buffer += L" "; buffer += arg_esc; @@ -524,10 +381,8 @@ 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 - */ + // 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(); @@ -540,39 +395,24 @@ static void event_fire_internal(const event_t &event) proc_set_last_status(prev_status); } - /* - Free killed events - */ - if (is_event <= 1) - event_free_kills(); - + // Free killed events. + if (is_event <= 1) event_free_kills(); } -/** - Handle all pending signal events -*/ -static void event_fire_delayed() -{ - /* - 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) - { +/// Handle all pending signal events. +static void event_fire_delayed() { + // 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 (size_t i=0; i 0) - { + while (sig_list[al].count > 0) { signal_list_t *lst; - /* - Switch signal lists - */ - sig_list[1-al].count=0; - sig_list[1-al].overflow=0; - al = 1-al; - active_list=al; + // Switch signal lists. + sig_list[1 - al].count = 0; + sig_list[1 - al].overflow = 0; + al = 1 - al; + active_list = al; - /* - Set up - */ - lst = &sig_list[1-al]; + // Set up. + lst = &sig_list[1 - al]; event_t e = event_t::signal_event(0); e.arguments.resize(1); - if (lst->overflow) - { + 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++) - { + // Send all signals in our private list. + for (int i = 0; i < lst->count; i++) { e.param1.signal = lst->signal[i]; e.arguments.at(0) = sig2wcs(e.param1.signal); - if (event_is_blocked(e)) - { + if (event_is_blocked(e)) { blocked.push_back(new event_t(e)); - } - else - { + } else { event_fire_internal(e); } } - } } -void event_fire_signal(int signal) -{ - /* - This means we are in a signal handler. We must be very - careful not do do anything that could cause a memory - allocation or something else that might be bad when in a - signal handler. - */ +void event_fire_signal(int signal) { + // This means we are in a signal handler. We must be very careful not do do anything that could + // cause a memory allocation or something else that might be bad when in a signal handler. if (sig_list[active_list].count < SIG_UNHANDLED_MAX) - sig_list[active_list].signal[sig_list[active_list].count++]=signal; + sig_list[active_list].signal[sig_list[active_list].count++] = signal; else - sig_list[active_list].overflow=1; + sig_list[active_list].overflow = 1; } - -void event_fire(const event_t *event) -{ - - if (event && event->type == EVENT_SIGNAL) - { +void event_fire(const event_t *event) { + if (event && event->type == EVENT_SIGNAL) { event_fire_signal(event->param1.signal); - } - else - { + } else { is_event++; - /* - Fire events triggered by signals - */ + // 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(new event_t(*event)); - } - else - { + } else { event_fire_internal(*event); } } @@ -672,14 +482,9 @@ void event_fire(const event_t *event) } } +void event_init() {} -void event_init() -{ -} - -void event_destroy() -{ - +void event_destroy() { for_each(s_event_handlers.begin(), s_event_handlers.end(), event_free); s_event_handlers.clear(); @@ -687,49 +492,38 @@ void event_destroy() killme.clear(); } -void event_free(event_t *e) -{ - CHECK(e,); +void event_free(event_t *e) { + CHECK(e, ); delete e; } -void event_fire_generic(const wchar_t *name, wcstring_list_t *args) -{ - CHECK(name,); +void event_fire_generic(const wchar_t *name, wcstring_list_t *args) { + CHECK(name, ); event_t ev(EVENT_GENERIC); ev.str_param1 = name; - if (args) - ev.arguments = *args; + if (args) ev.arguments = *args; event_fire(&ev); } -event_t::event_t(int t) : type(t), param1(), str_param1(), function_name(), arguments() -{ -} +event_t::event_t(int t) : type(t), param1(), str_param1(), function_name(), arguments() {} -event_t::~event_t() -{ -} +event_t::~event_t() {} -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; } - diff --git a/src/event.h b/src/event.h index 93051fac4..1e48efd46 100644 --- a/src/event.h +++ b/src/event.h @@ -1,91 +1,76 @@ -/** \file event.h - - Functions for handling event triggers - - Because most of these functions can be called by signal - handler, it is important to make it well defined when these - functions produce output or perform memory allocations, since - such functions may not be safely called by signal handlers. - - -*/ +// Functions for handling event triggers +// +// Because most of these functions can be called by signal handler, it is important to make it well +// defined when these functions produce output or perform memory allocations, since such functions +// may not be safely called by signal handlers. #ifndef FISH_EVENT_H #define FISH_EVENT_H +#include #include #include -#include #include "common.h" -/** - The signal number that is used to match any signal -*/ +/// The signal number that is used to match any signal. #define EVENT_ANY_SIGNAL -1 -/** - The process id that is used to match any process id -*/ +/// The process id that is used to match any process id. #define EVENT_ANY_PID 0 -/** - Enumeration of event types -*/ -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 */ -} -; +/// Enumeration of event types. +enum { + /// Matches any event type (Not always any event, as the function name may limit the choice as + /// well. + EVENT_ANY, + /// An event triggered by a signal. + EVENT_SIGNAL, + /// An event triggered by a variable update. + EVENT_VARIABLE, + /// An event triggered by a job or process exit. + EVENT_EXIT, + /// An event triggered by a job exit. + EVENT_JOB_ID, + /// A generic event. + EVENT_GENERIC, +}; -/** - The structure which represents an event. The event_t struct has - several event-related use-cases: - - - When used as a parameter to event_add, it represents a class of events, and function_name is the name of the function which will be called whenever an event matching the specified class occurs. This is also how events are stored internally. - - When used as a parameter to event_get, event_remove and event_fire, it represents a class of events, and if the function_name field is non-zero, only events which call the specified function will be returned. -*/ -struct event_t -{ -public: - - /** Type of event */ +/// The structure which represents an event. The event_t struct has several event-related use-cases: +/// +/// - When used as a parameter to event_add, it represents a class of events, and function_name is +/// the name of the function which will be called whenever an event matching the specified class +/// occurs. This is also how events are stored internally. +/// +/// - When used as a parameter to event_get, event_remove and event_fire, it represents a class of +/// events, and if the function_name field is non-zero, only events which call the specified +/// function will be returned. +struct event_t { + public: + /// 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 - */ - union - { + /// The type-specific parameter. The int types are one of the following: + /// + /// signal: Signal number for signal-type events.Use EVENT_ANY_SIGNAL to match any signal + /// pid: Process id for process-type events. Use EVENT_ANY_PID to match any pid. + /// job_id: Job id for EVENT_JOB_ID type events + union { int signal; int job_id; pid_t pid; } param1; - /** The string types are one of the following: - - variable: Variable name for variable-type events. - param: The parameter describing this generic event. - */ + /// The string types are one of the following: + /// + /// variable: Variable name for variable-type events. + /// param: The parameter describing this generic event. wcstring str_param1; - /** - The name of the event handler function - */ + /// 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. wcstring_list_t arguments; explicit event_t(int t); @@ -96,83 +81,61 @@ struct event_t static event_t generic_event(const wcstring &str); }; -/** - Add an event handler - - May not be called by a signal handler, since it may allocate new memory. -*/ +/// Add an event handler. +/// +/// May not be called by a signal handler, since it may allocate new memory. void event_add_handler(const event_t &event); -/** - Remove all events matching the specified criterion. - - May not be called by a signal handler, since it may free allocated memory. -*/ +/// Remove all events matching the specified criterion. +/// +/// May not be called by a signal handler, since it may free allocated memory. void event_remove(const event_t &event); -/** - Return all events which match the specified event class - - This function is safe to call from a signal handler _ONLY_ if the - out parameter is null. - - \param criterion Is the class of events to return. If the criterion has a non-null function_name, only events which trigger the specified function will return. - \param out the list to add events to. May be 0, in which case no events will be added, but the result count will still be valid - - \return the number of found matches -*/ +/// Return all events which match the specified event class +/// +/// This function is safe to call from a signal handler _ONLY_ if the out parameter is null. +/// +/// \param criterion Is the class of events to return. If the criterion has a non-null +/// function_name, only events which trigger the specified function will return. +/// \param out the list to add events to. May be 0, in which case no events will be added, but the +/// result count will still be valid +/// +/// \return the number of found matches int event_get(const event_t &criterion, std::vector *out); -/** - Returns whether an event listener is registered for the given signal. - This is safe to call from a signal handler. -*/ +/// Returns whether an event listener is registered for the given signal. This is safe to call from +/// a signal handler. bool event_is_signal_observed(int signal); -/** - Fire the specified event. The function_name field of the event must - be set to 0. If the event is of type EVENT_SIGNAL, no the event is - queued, and will be dispatched the next time event_fire is - called. If event is a null-pointer, all pending events are - dispatched. - - This function is safe to call from a signal handler _ONLY_ if the - event parameter is for a signal. Signal events not be fired, by the - call to event_fire, instead they will be fired the next time - event_fire is called with anull argument. This is needed to make - sure that no code evaluation is ever performed by a signal handler. - - \param event the specific event whose handlers should fire. If - null, then all delayed events will be fired. -*/ +/// Fire the specified event. The function_name field of the event must be set to 0. If the event is +/// of type EVENT_SIGNAL, no the event is queued, and will be dispatched the next time event_fire is +/// called. If event is a null-pointer, all pending events are dispatched. +/// +/// This function is safe to call from a signal handler _ONLY_ if the event parameter is for a +/// signal. Signal events not be fired, by the call to event_fire, instead they will be fired the +/// next time event_fire is called with anull argument. This is needed to make sure that no code +/// evaluation is ever performed by a signal handler. +/// +/// \param event the specific event whose handlers should fire. If null, then all delayed events +/// will be fired. void event_fire(const event_t *event); -/** Like event_fire, but takes a signal directly. */ +/// Like event_fire, but takes a signal directly. void event_fire_signal(int signal); -/** - Initialize the event-handling library -*/ +/// Initialize the event-handling library. void event_init(); -/** - Destroy the event-handling library -*/ +/// Destroy the event-handling library. void event_destroy(); -/** - Free all memory used by the specified event -*/ +/// Free all memory used by the specified event. void event_free(event_t *e); -/** - Returns a string describing the specified event. -*/ +/// Returns a string describing the specified event. wcstring event_get_desc(const event_t &e); -/** - Fire a generic event with the specified name -*/ +/// Fire a generic event with the specified name. void event_fire_generic(const wchar_t *name, wcstring_list_t *args = NULL); #endif From d93bbfd4860b3d8832d98285787fa6394cef16a1 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Thu, 28 Apr 2016 19:41:54 -0700 Subject: [PATCH 157/363] restyle exec module to match project style Reduces lint errors from 121 to 59 (-51%). Line count from 1578 to 1290 (-18%). Another step in resolving issue #2902. --- src/exec.cpp | 1388 ++++++++++++++++++++------------------------------ src/exec.h | 76 +-- 2 files changed, 588 insertions(+), 876 deletions(-) diff --git a/src/exec.cpp b/src/exec.cpp index 02d1b7244..b7735d62e 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -1,74 +1,62 @@ -/** \file exec.c - Functions for executing a program. - - Some of the code in this file is based on code from the Glibc - manual, though the changes performed have been massive. -*/ +// Functions for executing a program. +// +// Some of the code in this file is based on code from the Glibc manual, though the changes +// performed have been massive. #include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include #include -#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include #ifdef HAVE_SPAWN_H #include #endif #include #include -#include #include +#include #ifdef HAVE_SIGINFO_H #include #endif #include -#include "fallback.h" // IWYU pragma: keep -#include "postfork.h" -#include "common.h" -#include "wutil.h" // IWYU pragma: keep -#include "proc.h" -#include "exec.h" -#include "parser.h" #include "builtin.h" -#include "function.h" +#include "common.h" #include "env.h" -#include "signal.h" +#include "exec.h" +#include "fallback.h" // IWYU pragma: keep +#include "function.h" #include "io.h" #include "parse_tree.h" +#include "parser.h" +#include "postfork.h" +#include "proc.h" #include "reader.h" +#include "signal.h" +#include "wutil.h" // IWYU pragma: keep -/** - file descriptor redirection error message -*/ -#define FD_ERROR _( L"An error occurred while redirecting file descriptor %d" ) +/// File descriptor redirection error message. +#define FD_ERROR _(L"An error occurred while redirecting file descriptor %d") -/** - file descriptor redirection error message -*/ -#define WRITE_ERROR _( L"An error occurred while writing output" ) +/// File descriptor redirection error message. +#define WRITE_ERROR _(L"An error occurred while writing output") -/** - file redirection error message -*/ -#define FILE_ERROR _( L"An error occurred while redirecting file '%s'" ) +/// File redirection error message. +#define FILE_ERROR _(L"An error occurred while redirecting file '%s'") -/** - Base open mode to pass to calls to open -*/ +/// Base open mode to pass to calls to open. #define OPEN_MASK 0666 -// Called in a forked child -static void exec_write_and_exit(int fd, const char *buff, size_t count, int status) -{ - if (write_loop(fd, buff, count) == -1) - { +/// Called in a forked child. +static void exec_write_and_exit(int fd, const char *buff, size_t count, int status) { + if (write_loop(fd, buff, count) == -1) { debug(0, WRITE_ERROR); wperror(L"write"); exit_without_destructors(status); @@ -76,22 +64,17 @@ static void exec_write_and_exit(int fd, const char *buff, size_t count, int stat 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) - { + // 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) - { + while (close(fd) == -1) { + if (errno != EINTR) { debug(1, FD_ERROR, fd); wperror(L"close"); break; @@ -99,56 +82,45 @@ void exec_close(int fd) } } -int exec_pipe(int fd[2]) -{ +int exec_pipe(int fd[2]) { ASSERT_IS_MAIN_THREAD(); int res; - while ((res=pipe(fd))) - { - if (errno != EINTR) - { - // caller will call wperror - return res; + while ((res = pipe(fd))) { + if (errno != EINTR) { + return res; // caller will call wperror } } debug(4, L"Created pipe using fds %d and %d", fd[0], fd[1]); - - // Pipes ought to be cloexec - // Pipes are dup2'd the corresponding fds; the resulting fds are not cloexec + + // Pipes ought to be cloexec. Pipes are dup2'd the corresponding fds; the resulting fds are not + // cloexec. set_cloexec(fd[0]); set_cloexec(fd[1]); return res; } -/* Returns true if the redirection is a file redirection to a file other than /dev/null */ -static bool redirection_is_to_real_file(const io_data_t *io) -{ +/// Returns true if the redirection is a file redirection to a file other than /dev/null. +static bool redirection_is_to_real_file(const io_data_t *io) { bool result = false; - if (io != NULL && io->io_mode == IO_FILE) - { - /* It's a file redirection. Compare the path to /dev/null */ + if (io != NULL && io->io_mode == IO_FILE) { + // It's a file redirection. Compare the path to /dev/null. CAST_INIT(const io_file_t *, io_file, io); const char *path = io_file->filename_cstr; - if (strcmp(path, "/dev/null") != 0) - { - /* It's not /dev/null */ + if (strcmp(path, "/dev/null") != 0) { + // It's not /dev/null. result = true; } - } return result; } -static bool chain_contains_redirection_to_real_file(const io_chain_t &io_chain) -{ +static bool chain_contains_redirection_to_real_file(const io_chain_t &io_chain) { bool result = false; - for (size_t idx=0; idx < io_chain.size(); idx++) - { + for (size_t idx = 0; idx < io_chain.size(); idx++) { const io_data_t *io = io_chain.at(idx).get(); - if (redirection_is_to_real_file(io)) - { + if (redirection_is_to_real_file(io)) { result = true; break; } @@ -156,89 +128,68 @@ static bool chain_contains_redirection_to_real_file(const io_chain_t &io_chain) return result; } -/** - 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) -{ - // OK to not use CLO_EXEC here because this is only called after fork +/// 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) { + // OK to not use CLO_EXEC here because this is only called after fork. int fd = open(command, O_RDONLY); - if (fd >= 0) - { + if (fd >= 0) { size_t idx = 0; - while (idx + 1 < buff_size) - { + while (idx + 1 < buff_size) { char ch; ssize_t amt = read(fd, &ch, sizeof ch); - if (amt <= 0) - break; - if (ch == '\n') - break; + if (amt <= 0) break; + if (ch == '\n') break; interpreter[idx++] = ch; } interpreter[idx++] = '\0'; close(fd); } - if (strncmp(interpreter, "#! /", 4) == 0) - { + if (strncmp(interpreter, "#! /", 4) == 0) { return interpreter + 3; - } - else if (strncmp(interpreter, "#!/", 3) == 0) - { + } else if (strncmp(interpreter, "#!/", 3) == 0) { return interpreter + 2; - } - else - { + } else { return NULL; } } -/** - This function is executed by the child process created by a call to - fork(). It should be called after \c setup_child_process. It calls - execve to replace the fish process image with the command specified - 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, const char *const* cargv, const char *const *cenvv) -{ +/// This function is executed by the child process created by a call to fork(). It should be called +/// after \c setup_child_process. It calls execve to replace the fish process image with the command +/// specified 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, const char *const *cargv, + const char *const *cenvv) { int err; + // debug( 1, L"exec '%ls'", p->argv[0] ); -// debug( 1, L"exec '%ls'", p->argv[0] ); - - // This function never returns, so we take certain liberties with constness - char * const * envv = const_cast(cenvv); - char * const * argv = const_cast(cargv); + // This function never returns, so we take certain liberties with constness. + char *const *envv = const_cast(cenvv); + char *const *argv = const_cast(cargv); execve(actual_cmd, argv, envv); err = errno; - /* - Something went wrong with execve, check for a ":", and run - /bin/sh if encountered. This is a weird predecessor to the shebang - that is still sometimes used since it is supported on Windows. - */ - /* OK to not use CLO_EXEC here because this is called after fork and the file is immediately closed */ + // 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) - { + 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. + 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++) - { - argv2[i] = argv[i-1]; - if (argv2[i] == NULL) - break; + 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); @@ -250,12 +201,9 @@ static void safe_launch_process(process_t *p, const char *actual_cmd, const char 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) -{ +/// 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) { ASSERT_IS_MAIN_THREAD(); ASSERT_IS_NOT_FORKED_CHILD(); @@ -265,96 +213,71 @@ static void launch_process_nofork(process_t *p) const char *const *envv = env_export_arr(false); char *actual_cmd = wcs2str(p->actual_cmd.c_str()); - /* Ensure the terminal modes are what they were before we changed them. */ + // Ensure the terminal modes are what they were before we changed them. restore_term_mode(); - /* Bounce to launch_process. This never returns. */ + // Bounce to launch_process. This never returns. safe_launch_process(p, actual_cmd, argv_array.get(), envv); } +/// Check if the IO redirection chains contains redirections for the specified file descriptor. +static int has_fd(const io_chain_t &d, int fd) { return io_chain_get(d, fd).get() != NULL; } -/** - Check if the IO redirection chains contains redirections for the - specified file descriptor -*/ -static int has_fd(const io_chain_t &d, int fd) -{ - return io_chain_get(d, fd).get() != NULL; -} - -/** - Close a list of fds. -*/ -static void io_cleanup_fds(const std::vector &opened_fds) -{ +/// Close a list of fds. +static void io_cleanup_fds(const std::vector &opened_fds) { std::for_each(opened_fds.begin(), opened_fds.end(), close); } -/** - Make a copy of the specified io redirection chain, but change file - redirection into fd redirection. This makes the redirection chain - suitable for use as block-level io, since the file won't be - repeatedly reopened for every command in the block, which would - reset the cursor position. - - \return true on success, false on failure. Returns the output chain and opened_fds by reference -*/ -static bool io_transmogrify(const io_chain_t &in_chain, io_chain_t *out_chain, std::vector *out_opened_fds) -{ +/// Make a copy of the specified io redirection chain, but change file redirection into fd +/// redirection. This makes the redirection chain suitable for use as block-level io, since the file +/// won't be repeatedly reopened for every command in the block, which would reset the cursor +/// position. +/// +/// \return true on success, false on failure. Returns the output chain and opened_fds by reference. +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 != NULL && out_opened_fds != NULL); assert(out_chain->empty()); - /* Just to be clear what we do for an empty chain */ - if (in_chain.empty()) - { + // Just to be clear what we do for an empty chain. + if (in_chain.empty()) { return true; } bool success = true; - /* Make our chain of redirections */ + // Make our chain of redirections. io_chain_t result_chain; - /* In the event we can't finish transmorgrifying, we'll have to close all the files we opened. */ + // In the event we can't finish transmorgrifying, we'll have to close all the files we opened. std::vector opened_fds; - for (size_t idx = 0; idx < in_chain.size(); idx++) - { + for (size_t idx = 0; idx < in_chain.size(); idx++) { const shared_ptr &in = in_chain.at(idx); - shared_ptr out; //gets allocated via new + shared_ptr out; // gets allocated via new - switch (in->io_mode) - { + switch (in->io_mode) { default: - /* Unknown type, should never happen */ + // 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. - */ + // 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_CLOSE: { out = in; break; } - /* - Transmogrify file redirections - */ - case IO_FILE: - { + // Transmogrify file redirections. + case IO_FILE: { int fd; CAST_INIT(io_file_t *, in_file, in.get()); - if ((fd=open(in_file->filename_cstr, in_file->flags, OPEN_MASK))==-1) - { - debug(1, - FILE_ERROR, - in_file->filename_cstr); + if ((fd = open(in_file->filename_cstr, in_file->flags, OPEN_MASK)) == -1) { + debug(1, FILE_ERROR, in_file->filename_cstr); wperror(L"open"); success = false; @@ -363,78 +286,56 @@ static bool io_transmogrify(const io_chain_t &in_chain, io_chain_t *out_chain, s opened_fds.push_back(fd); out.reset(new io_fd_t(in->fd, fd, false)); - break; } } - if (out.get() != NULL) - result_chain.push_back(out); + if (out.get() != NULL) result_chain.push_back(out); - /* Don't go any further if we failed */ - if (! success) - { + // Don't go any further if we failed. + if (!success) { break; } } - /* Now either return success, or clean up */ - if (success) - { - /* Yay */ + // Now either return success, or clean up. + if (success) { out_chain->swap(result_chain); out_opened_fds->swap(opened_fds); - } - else - { - /* No dice - clean up */ + } else { result_chain.clear(); io_cleanup_fds(opened_fds); } return success; } - -/** - Morph an io redirection chain into redirections suitable for - passing to eval, call eval, and clean up morphed redirections. - - \param def the code to evaluate, or the empty string if none - \param node_offset the offset of the node to evalute, or NODE_OFFSET_INVALID - \param block_type the type of block to push on evaluation - \param io the io redirections to be performed on this block -*/ - -static void internal_exec_helper(parser_t &parser, - const wcstring &def, - node_offset_t node_offset, - enum block_type_t block_type, - const io_chain_t &ios) -{ - // If we have a valid node offset, then we must not have a string to execute +/// Morph an io redirection chain into redirections suitable for passing to eval, call eval, and +/// clean up morphed redirections. +/// +/// \param def the code to evaluate, or the empty string if none +/// \param node_offset the offset of the node to evalute, or NODE_OFFSET_INVALID +/// \param block_type the type of block to push on evaluation +/// \param io the io redirections to be performed on this block +static void internal_exec_helper(parser_t &parser, const wcstring &def, node_offset_t node_offset, + enum block_type_t block_type, const io_chain_t &ios) { + // If we have a valid node offset, then we must not have a string to execute. assert(node_offset == NODE_OFFSET_INVALID || def.empty()); io_chain_t morphed_chain; std::vector opened_fds; bool transmorgrified = io_transmogrify(ios, &morphed_chain, &opened_fds); - /* - Did the transmogrification fail - if so, set error status and return - */ - if (! transmorgrified) - { + // Did the transmogrification fail - if so, set error status and return. + if (!transmorgrified) { proc_set_last_status(STATUS_EXEC_FAIL); return; } signal_unblock(); - if (node_offset == NODE_OFFSET_INVALID) - { + if (node_offset == NODE_OFFSET_INVALID) { parser.eval(def, morphed_chain, block_type); - } - else - { + } else { parser.eval_block_node(node_offset, morphed_chain, block_type); } @@ -445,54 +346,50 @@ static void internal_exec_helper(parser_t &parser, job_reap(0); } -/* Returns whether we can use posix spawn for a given process in a given job. - Per https://github.com/fish-shell/fish-shell/issues/364 , error handling for file redirections is too difficult with posix_spawn, - so in that case we use fork/exec. - - Furthermore, to avoid the race between the caller calling tcsetpgrp() and the client checking the foreground process group, we don't use posix_spawn if we're going to foreground the process. (If we use fork(), we can call tcsetpgrp after the fork, before the exec, and avoid the race). -*/ -static bool can_use_posix_spawn_for_job(const job_t *job, const process_t *process) -{ - 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)) - { - /* It will be foregrounded, so we will call tcsetpgrp(), therefore do not use posix_spawn */ +// Returns whether we can use posix spawn for a given process in a given job. Per +// https://github.com/fish-shell/fish-shell/issues/364 , error handling for file redirections is too +// difficult with posix_spawn, so in that case we use fork/exec. +// +// Furthermore, to avoid the race between the caller calling tcsetpgrp() and the client checking the +// foreground process group, we don't use posix_spawn if we're going to foreground the process. (If +// we use fork(), we can call tcsetpgrp after the fork, before the exec, and avoid the race). +static bool can_use_posix_spawn_for_job(const job_t *job, const process_t *process) { + 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)) { + // It will be foregrounded, so we will call tcsetpgrp(), therefore do not use + // posix_spawn. return false; } } - /* Now see if we have a redirection involving a file. The only one we allow is /dev/null, which we assume will not fail. */ + // Now see if we have a redirection involving a file. The only one we allow is /dev/null, which + // we assume will not fail. bool result = true; - if (chain_contains_redirection_to_real_file(job->block_io_chain()) || chain_contains_redirection_to_real_file(process->io_chain())) - { + if (chain_contains_redirection_to_real_file(job->block_io_chain()) || + chain_contains_redirection_to_real_file(process->io_chain())) { result = false; } return result; } -void exec_job(parser_t &parser, job_t *j) -{ +void exec_job(parser_t &parser, job_t *j) { pid_t pid = 0; sigset_t chldset; - /* - Set to true if something goes wrong while exec:ing the job, in - which case the cleanup code will kick in. - */ + // 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; - - CHECK(j,); + CHECK(j, ); CHECK_BLOCK(); - /* If fish was invoked with -n or --no-execute, then no_exec will be set and we do nothing. */ - if (no_exec) - { + // If fish was invoked with -n or --no-execute, then no_exec will be set and we do nothing. + if (no_exec) { return; } @@ -501,238 +398,199 @@ void exec_job(parser_t &parser, job_t *j) debug(4, L"Exec job '%ls' with id %d", j->command_wcstr(), j->job_id); - /* Verify that all IO_BUFFERs are output. We used to support a (single, hacked-in) magical input IO_BUFFER used by fish_pager, but now the claim is that there are no more clients and it is removed. This assertion double-checks that. */ + // Verify that all IO_BUFFERs are output. We used to support a (single, hacked-in) magical input + // IO_BUFFER used by fish_pager, but now the claim is that there are no more clients and it is + // removed. This assertion double-checks that. const io_chain_t all_ios = j->all_io_redirections(); - for (size_t idx = 0; idx < all_ios.size(); idx++) - { + for (size_t idx = 0; idx < all_ios.size(); idx++) { const shared_ptr &io = all_ios.at(idx); - if ((io->io_mode == IO_BUFFER)) - { + if ((io->io_mode == IO_BUFFER)) { CAST_INIT(io_buffer_t *, io_buffer, io.get()); - assert(! io_buffer->is_input); + assert(!io_buffer->is_input); } } - if (j->first_process->type==INTERNAL_EXEC) - { - /* - Do a regular launch - but without forking first... - */ + 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 - */ + // setup_child_process makes sure signals are properly set up. It will also call + // signal_unblock. - /* PCA This is for handling exec. Passing all_ios here matches what fish 2.0.0 and 1.x did. It's known to be wrong - for example, it means that redirections bound for subsequent commands in the pipeline will apply to exec. However, using exec in a pipeline doesn't really make sense, so I'm not trying to fix it here. */ - if (!setup_child_process(j, 0, all_ios)) - { - /* decrement SHLVL as we're removing ourselves from the shell "stack" */ + // PCA This is for handling exec. Passing all_ios here matches what fish 2.0.0 and 1.x did. + // It's known to be wrong - for example, it means that redirections bound for subsequent + // commands in the pipeline will apply to exec. However, using exec in a pipeline doesn't + // really make sense, so I'm not trying to fix it here. + if (!setup_child_process(j, 0, all_ios)) { + // Decrement SHLVL as we're removing ourselves from the shell "stack". const env_var_t shlvl_str = env_get_string(L"SHLVL", ENV_GLOBAL | ENV_EXPORT); wcstring nshlvl_str = L"0"; - if (!shlvl_str.missing()) - { + if (!shlvl_str.missing()) { wchar_t *end; long shlvl_i = wcstol(shlvl_str.c_str(), &end, 10); - while (iswspace(*end)) ++end; /* skip trailing whitespace */ - if (shlvl_i > 0 && *end == '\0') - { + while (iswspace(*end)) ++end; // skip trailing whitespace + if (shlvl_i > 0 && *end == '\0') { nshlvl_str = to_string(shlvl_i - 1); } } env_set(L"SHLVL", nshlvl_str.c_str(), ENV_GLOBAL | ENV_EXPORT); - /* - launch_process _never_ returns - */ + // launch_process _never_ returns. launch_process_nofork(j->first_process); - } - else - { + } else { job_set_flag(j, JOB_CONSTRUCTED, 1); - j->first_process->completed=1; + j->first_process->completed = 1; return; } assert(0 && "This should be unreachable"); } - - /* We may have block IOs that conflict with fd redirections. For example, we may have a command with a redireciton like <&3; we may also have chosen 3 as the fd for our pipe. Ensure we have no conflicts. */ - for (size_t i=0; i < all_ios.size(); i++) - { + + // We may have block IOs that conflict with fd redirections. For example, we may have a command + // with a redireciton like <&3; we may also have chosen 3 as the fd for our pipe. Ensure we have + // no conflicts. + for (size_t i = 0; i < all_ios.size(); i++) { io_data_t *io = all_ios.at(i).get(); - if (io->io_mode == IO_BUFFER) - { + if (io->io_mode == IO_BUFFER) { CAST_INIT(io_buffer_t *, io_buffer, io); - if (! io_buffer->avoid_conflicts_with_io_chain(all_ios)) - { - /* We could not avoid conflicts, probably due to fd exhaustion. Mark an error. */ + if (!io_buffer->avoid_conflicts_with_io_chain(all_ios)) { + // We could not avoid conflicts, probably due to fd exhaustion. Mark an error. exec_error = true; job_mark_process_as_failed(j, j->first_process); break; } } } - signal_block(); - /* - See if we need to create a group keepalive process. This is - a process that we create to make sure that the process group - doesn't die accidentally, and is often needed when a - builtin/block/function is inside a pipeline, since that - usually means we have to wait for one program to exit before - continuing in the pipeline, causing the group leader to - exit. - */ - - if (job_get_flag(j, JOB_CONTROL) && ! exec_error) - { - for (const process_t *p = j->first_process; p; p = p->next) - { - if (p->type != EXTERNAL) - { - if (p->next) - { + // 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) && !exec_error) { + for (const process_t *p = j->first_process; p; p = p->next) { + if (p->type != EXTERNAL) { + if (p->next) { needs_keepalive = true; break; } - if (p != j->first_process) - { + if (p != j->first_process) { needs_keepalive = true; break; } - } - } } - if (needs_keepalive) - { - /* Call fork. No need to wait for threads since our use is confined and simple. */ - if (g_log_forks) - { - printf("fork #%d: Executing keepalive fork for '%ls'\n", g_fork_count, j->command_wcstr()); + if (needs_keepalive) { + // Call fork. No need to wait for threads since our use is confined and simple. + if (g_log_forks) { + printf("fork #%d: Executing keepalive fork for '%ls'\n", g_fork_count, + j->command_wcstr()); } keepalive.pid = execute_fork(false); - if (keepalive.pid == 0) - { + if (keepalive.pid == 0) { /* Child */ keepalive.pid = getpid(); set_child_group(j, &keepalive, 1); pause(); exit_without_destructors(0); - } - else - { + } else { /* Parent */ set_child_group(j, &keepalive, 0); } } - /* - This loop loops over every process_t in the job, starting it as - appropriate. This turns out to be rather complex, since a - process_t can be one of many rather different things. - - The loop also has to handle pipelining between the jobs. - */ - - /* We can have up to three pipes "in flight" at a time: - - 1. The pipe the current process should read from (courtesy of the previous process) - 2. The pipe that the current process should write to - 3. The pipe that the next process should read from (courtesy of us) - - We are careful to set these to -1 when closed, so if we exit the loop abruptly, we can still close them. - - */ - + // 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. + // + // We can have up to three pipes "in flight" at a time: + // + // 1. The pipe the current process should read from (courtesy of the previous process) + // 2. The pipe that the current process should write to + // 3. The pipe that the next process should read from (courtesy of us) + // + // We are careful to set these to -1 when closed, so if we exit the loop abruptly, we can still + // close them. int pipe_current_read = -1, pipe_current_write = -1, pipe_next_read = -1; - for (process_t *p=j->first_process; p != NULL && ! exec_error; p = p->next) - { - /* The IO chain for this process. It starts with the block IO, then pipes, and then gets any from the process */ + for (process_t *p = j->first_process; p != NULL && !exec_error; p = p->next) { + // The IO chain for this process. It starts with the block IO, then pipes, and then gets any + // from the process. io_chain_t process_net_io_chain = j->block_io_chain(); - /* "Consume" any pipe_next_read by making it current */ + // "Consume" any pipe_next_read by making it current. assert(pipe_current_read == -1); pipe_current_read = pipe_next_read; pipe_next_read = -1; - /* See if we need a pipe */ + // See if we need a pipe. const bool pipes_to_next_command = (p->next != NULL); - /* The pipes the current process write to and read from. - Unfortunately these can't be just allocated on the stack, since - j->io wants shared_ptr. - - The write pipe (destined for stdout) needs to occur before redirections. For example, with a redirection like this: - `foo 2>&1 | bar`, what we want to happen is this: - - dup2(pipe, stdout) - dup2(stdout, stderr) - - so that stdout and stderr both wind up referencing the pipe. - - The read pipe (destined for stdin) is more ambiguous. Imagine a pipeline like this: - - echo alpha | cat < beta.txt - - Should cat output alpha or beta? bash and ksh output 'beta', tcsh gets it right and complains about ambiguity, and zsh outputs both (!). No shells appear to output 'alpha', so we match bash here. That would mean putting the pipe first, so that it gets trumped by the file redirection. - - However, eval does this: - - echo "begin; $argv "\n" ;end <&3 3<&-" | source 3<&0 - - which depends on the redirection being evaluated before the pipe. So the write end of the pipe comes first, the read pipe of the pipe comes last. See issue #966. - */ - + // The pipes the current process write to and read from. Unfortunately these can't be just + // allocated on the stack, since j->io wants shared_ptr. + // + // The write pipe (destined for stdout) needs to occur before redirections. For example, + // with a redirection like this: + // + // `foo 2>&1 | bar` + // + // what we want to happen is this: + // + // dup2(pipe, stdout) + // dup2(stdout, stderr) + // + // so that stdout and stderr both wind up referencing the pipe. + // + // The read pipe (destined for stdin) is more ambiguous. Imagine a pipeline like this: + // + // echo alpha | cat < beta.txt + // + // Should cat output alpha or beta? bash and ksh output 'beta', tcsh gets it right and + // complains about ambiguity, and zsh outputs both (!). No shells appear to output 'alpha', + // so we match bash here. That would mean putting the pipe first, so that it gets trumped by + // the file redirection. + // + // However, eval does this: + // + // echo "begin; $argv "\n" ;end <&3 3<&-" | source 3<&0 + // + // which depends on the redirection being evaluated before the pipe. So the write end of the + // pipe comes first, the read pipe of the pipe comes last. See issue #966. shared_ptr pipe_write; shared_ptr pipe_read; - /* Write pipe goes first */ - if (p->next) - { + // Write pipe goes first. + if (p->next) { pipe_write.reset(new io_pipe_t(p->pipe_write_fd, false)); process_net_io_chain.push_back(pipe_write); } - /* The explicit IO redirections associated with the process */ + // The explicit IO redirections associated with the process. process_net_io_chain.append(p->io_chain()); - /* Read pipe goes last */ - if (p != j->first_process) - { + // Read pipe goes last. + if (p != j->first_process) { pipe_read.reset(new io_pipe_t(p->pipe_read_fd, true)); - /* Record the current read in pipe_read */ + // Record the current read in pipe_read. pipe_read->pipe_fd[0] = pipe_current_read; process_net_io_chain.push_back(pipe_read); } + // 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 fds that will be used in the pipe. */ - if (pipes_to_next_command) - { -// debug( 1, L"%ls|%ls" , p->argv[0], p->next->argv[0]); + // Set up fds that will be used in the pipe. + if (pipes_to_next_command) { + // debug( 1, L"%ls|%ls" , p->argv[0], p->next->argv[0]); int local_pipe[2] = {-1, -1}; - if (exec_pipe(local_pipe) == -1) - { + if (exec_pipe(local_pipe) == -1) { debug(1, PIPE_ERROR); wperror(L"pipe"); exec_error = true; @@ -740,23 +598,23 @@ void exec_job(parser_t &parser, job_t *j) break; } - /* Ensure our pipe fds not conflict with any fd redirections. E.g. if the process is like 'cat <&5' then fd 5 must not be used by the pipe. */ - if (! pipe_avoid_conflicts_with_io_chain(local_pipe, all_ios)) - { - /* We failed. The pipes were closed for us. */ + // Ensure our pipe fds not conflict with any fd redirections. E.g. if the process is + // like 'cat <&5' then fd 5 must not be used by the pipe. + if (!pipe_avoid_conflicts_with_io_chain(local_pipe, all_ios)) { + // We failed. The pipes were closed for us. wperror(L"dup"); exec_error = true; job_mark_process_as_failed(j, p); break; } - // This tells the redirection about the fds, but the redirection does not close them + // This tells the redirection about the fds, but the redirection does not close them. assert(local_pipe[0] >= 0); assert(local_pipe[1] >= 0); - memcpy(pipe_write->pipe_fd, local_pipe, sizeof(int)*2); + memcpy(pipe_write->pipe_fd, local_pipe, sizeof(int) * 2); - // Record our pipes - // The fds should be negative to indicate that we aren't overwriting an fd we failed to close + // Record our pipes. The fds should be negative to indicate that we aren't overwriting + // an fd we failed to close. assert(pipe_current_write == -1); pipe_current_write = local_pipe[1]; @@ -764,70 +622,61 @@ void exec_job(parser_t &parser, job_t *j) pipe_next_read = local_pipe[0]; } - // This is the IO buffer we use for storing the output of a block or function when it is in a pipeline + // This is the IO buffer we use for storing the output of a block or function when it is in + // a pipeline. shared_ptr block_output_io_buffer; - - // This is the io_streams we pass to internal builtins - std::auto_ptr builtin_io_streams; - - switch (p->type) - { - case INTERNAL_FUNCTION: - { - /* - Calls to function_get_definition might need to - source a file as a part of autoloading, hence there - must be no blocks. - */ + // This is the io_streams we pass to internal builtins. + std::auto_ptr builtin_io_streams; + + switch (p->type) { + case INTERNAL_FUNCTION: { + // Calls to function_get_definition might need to source a file as a part of + // autoloading, hence there must be no blocks. signal_unblock(); const wcstring func_name = p->argv0(); wcstring def; bool function_exists = function_get_definition(func_name, &def); bool shadows = function_get_shadows(func_name); - const std::map inherit_vars = function_get_inherit_vars(func_name); + const std::map inherit_vars = + function_get_inherit_vars(func_name); signal_block(); - if (! function_exists) - { + if (!function_exists) { debug(0, _(L"Unknown function '%ls'"), p->argv0()); break; } function_block_t *newv = new function_block_t(p, func_name, shadows); parser.push_block(newv); - /* - setting variables might trigger an event - handler, hence we need to unblock - signals. - */ + // Setting variables might trigger an event handler, hence we need to unblock + // signals. signal_unblock(); - function_prepare_environment(func_name, p->get_argv()+1, inherit_vars); + function_prepare_environment(func_name, p->get_argv() + 1, inherit_vars); signal_block(); parser.forbid_function(func_name); - if (p->next) - { - // Be careful to handle failure, e.g. too many open fds + if (p->next) { + // Be careful to handle failure, e.g. too many open fds. block_output_io_buffer.reset(io_buffer_t::create(STDOUT_FILENO, all_ios)); - if (block_output_io_buffer.get() == NULL) - { + if (block_output_io_buffer.get() == NULL) { exec_error = true; job_mark_process_as_failed(j, p); - } - else - { - /* This looks sketchy, because we're adding this io buffer locally - they aren't in the process or job redirection list. Therefore select_try won't be able to read them. However we call block_output_io_buffer->read() below, which reads until EOF. So there's no need to select on this. */ + } else { + // This looks sketchy, because we're adding this io buffer locally - they + // aren't in the process or job redirection list. Therefore select_try won't + // be able to read them. However we call block_output_io_buffer->read() + // below, which reads until EOF. So there's no need to select on this. process_net_io_chain.push_back(block_output_io_buffer); } } - if (! exec_error) - { - internal_exec_helper(parser, def, NODE_OFFSET_INVALID, TOP, process_net_io_chain); + if (!exec_error) { + internal_exec_helper(parser, def, NODE_OFFSET_INVALID, TOP, + process_net_io_chain); } parser.allow_function(); @@ -836,235 +685,181 @@ void exec_job(parser_t &parser, job_t *j) break; } - case INTERNAL_BLOCK_NODE: - { - if (p->next) - { + case INTERNAL_BLOCK_NODE: { + if (p->next) { block_output_io_buffer.reset(io_buffer_t::create(STDOUT_FILENO, all_ios)); - if (block_output_io_buffer.get() == NULL) - { - /* We failed (e.g. no more fds could be created). */ + if (block_output_io_buffer.get() == NULL) { + // We failed (e.g. no more fds could be created). exec_error = true; job_mark_process_as_failed(j, p); - } - else - { - /* See the comment above about it's OK to add an IO redirection to this local buffer, even though it won't be handled in select_try */ + } else { + // See the comment above about it's OK to add an IO redirection to this + // local buffer, even though it won't be handled in select_try. process_net_io_chain.push_back(block_output_io_buffer); } } - if (! exec_error) - { - internal_exec_helper(parser, wcstring(), p->internal_block_node, TOP, process_net_io_chain); + if (!exec_error) { + internal_exec_helper(parser, wcstring(), p->internal_block_node, TOP, + process_net_io_chain); } break; } - case INTERNAL_BUILTIN: - { + case INTERNAL_BUILTIN: { int local_builtin_stdin = STDIN_FILENO; bool close_stdin = false; - /* - If this is the first process, check the io - redirections and see where we should be reading - from. - */ - if (p == j->first_process) - { - const shared_ptr in = process_net_io_chain.get_io_for_fd(STDIN_FILENO); + // If this is the first process, check the io redirections and see where we should + // be reading from. + if (p == j->first_process) { + const shared_ptr in = + process_net_io_chain.get_io_for_fd(STDIN_FILENO); - if (in) - { - switch (in->io_mode) - { - - case IO_FD: - { + if (in) { + switch (in->io_mode) { + case IO_FD: { CAST_INIT(const io_fd_t *, in_fd, in.get()); - /* Ignore user-supplied fd redirections from an fd other than the standard ones. e.g. in source <&3 don't actually read from fd 3, which is internal to fish. We still respect this redirection in that we pass it on as a block IO to the code that source runs, and therefore this is not an error. Non-user supplied fd redirections come about through transmogrification, and we need to respect those here. */ - if (! in_fd->user_supplied || (in_fd->old_fd >= 0 && in_fd->old_fd < 3)) - { + // Ignore user-supplied fd redirections from an fd other than the + // standard ones. e.g. in source <&3 don't actually read from fd 3, + // which is internal to fish. We still respect this redirection in + // that we pass it on as a block IO to the code that source runs, + // and therefore this is not an error. Non-user supplied fd + // redirections come about through transmogrification, and we need + // to respect those here. + if (!in_fd->user_supplied || + (in_fd->old_fd >= 0 && in_fd->old_fd < 3)) { local_builtin_stdin = in_fd->old_fd; } break; } - case IO_PIPE: - { + case IO_PIPE: { CAST_INIT(const io_pipe_t *, in_pipe, in.get()); local_builtin_stdin = in_pipe->pipe_fd[0]; break; } - - case IO_FILE: - { - /* Do not set CLO_EXEC because child needs access */ + case IO_FILE: { + // Do not set CLO_EXEC because child needs access. CAST_INIT(const io_file_t *, in_file, in.get()); - local_builtin_stdin=open(in_file->filename_cstr, - in_file->flags, OPEN_MASK); - if (local_builtin_stdin == -1) - { - debug(1, - FILE_ERROR, - in_file->filename_cstr); + local_builtin_stdin = + open(in_file->filename_cstr, in_file->flags, OPEN_MASK); + if (local_builtin_stdin == -1) { + debug(1, FILE_ERROR, in_file->filename_cstr); wperror(L"open"); - } - else - { + } else { close_stdin = true; } break; } - - case IO_CLOSE: - { - /* - FIXME: - - When requesting that stdin be closed, we - really don't do anything. How should this be - handled? - */ + case IO_CLOSE: { + // FIXME: When requesting that stdin be closed, we really don't do + // anything. How should this be handled? local_builtin_stdin = -1; break; } - - default: - { - local_builtin_stdin=-1; - debug(1, - _(L"Unknown input redirection type %d"), - in->io_mode); + default: { + local_builtin_stdin = -1; + debug(1, _(L"Unknown input redirection type %d"), in->io_mode); break; } - } } - } - else - { + } else { local_builtin_stdin = pipe_read->pipe_fd[0]; } - if (local_builtin_stdin == -1) - { + if (local_builtin_stdin == -1) { exec_error = true; break; - } - else - { - // Determine if we have a "direct" redirection for stdin + } else { + // Determine if we have a "direct" redirection for stdin. bool stdin_is_directly_redirected; - if (p != j->first_process) - { + if (p != j->first_process) { // We must have a pipe stdin_is_directly_redirected = true; - } - else - { - // We are not a pipe. Check if there is a redirection local to the process that's not IO_CLOSE - const shared_ptr stdin_io = io_chain_get(p->io_chain(), STDIN_FILENO); + } else { + // We are not a pipe. Check if there is a redirection local to the process + // that's not IO_CLOSE. + const shared_ptr stdin_io = + io_chain_get(p->io_chain(), STDIN_FILENO); stdin_is_directly_redirected = stdin_io && stdin_io->io_mode != IO_CLOSE; } - + builtin_io_streams.reset(new io_streams_t()); builtin_io_streams->stdin_fd = local_builtin_stdin; - builtin_io_streams->out_is_redirected = has_fd(process_net_io_chain, STDOUT_FILENO); - builtin_io_streams->err_is_redirected = has_fd(process_net_io_chain, STDERR_FILENO); + builtin_io_streams->out_is_redirected = + has_fd(process_net_io_chain, STDOUT_FILENO); + builtin_io_streams->err_is_redirected = + has_fd(process_net_io_chain, STDERR_FILENO); builtin_io_streams->stdin_is_directly_redirected = stdin_is_directly_redirected; builtin_io_streams->io_chain = &process_net_io_chain; - - /* - 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. - */ - + // 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. const int fg = job_get_flag(j, JOB_FOREGROUND); job_set_flag(j, JOB_FOREGROUND, 0); signal_unblock(); - + p->status = builtin_run(parser, p->get_argv(), *builtin_io_streams); signal_block(); - /* - Restore the fg flag, which is temporarily set to - false during builtin execution so as not to confuse - some job-handling builtins. - */ + // 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) - { + // If stdin has been redirected, close the redirection stream. + if (close_stdin) { exec_close(local_builtin_stdin); } break; } case EXTERNAL: - /* External commands are handled in the next switch statement below */ + // External commands are handled in the next switch statement below. break; case INTERNAL_EXEC: - /* We should have handled exec up above */ - assert(0 && "INTERNAL_EXEC process found in pipeline, where it should never be. Aborting."); + // We should have handled exec up above. + assert( + 0 && + "INTERNAL_EXEC process found in pipeline, where it should never be. Aborting."); break; } - if (exec_error) - { + if (exec_error) { break; } - switch (p->type) - { - + switch (p->type) { case INTERNAL_BLOCK_NODE: - case INTERNAL_FUNCTION: - { + 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 (! block_output_io_buffer.get()) - { - /* - No buffer, so we exit directly. This means we - have to manually set the exit status. - */ - if (p->next == NULL) - { - proc_set_last_status(job_get_flag(j, JOB_NEGATE)?(!status):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 (!block_output_io_buffer.get()) { + // No buffer, so we exit directly. This means we have to manually set the exit + // status. + if (p->next == NULL) { + proc_set_last_status(job_get_flag(j, JOB_NEGATE) ? (!status) : status); } p->completed = 1; break; } - // Here we must have a non-NULL block_output_io_buffer + // Here we must have a non-NULL block_output_io_buffer. assert(block_output_io_buffer.get() != NULL); process_net_io_chain.remove(block_output_io_buffer); @@ -1073,117 +868,102 @@ void exec_job(parser_t &parser, job_t *j) const char *buffer = block_output_io_buffer->out_buffer_ptr(); size_t count = block_output_io_buffer->out_buffer_size(); - if (block_output_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()); + if (block_output_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. - */ + if (pid == 0) { + // This is the child process. Write out the contents of the pipeline. p->pid = getpid(); setup_child_process(j, p, process_net_io_chain); exec_write_and_exit(block_output_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. - */ + } else { + // This is the parent process. Store away information on the child, and + // possibly give it control over the terminal. p->pid = pid; set_child_group(j, p, 0); - } - } - else - { - if (p->next == 0) - { - proc_set_last_status(job_get_flag(j, JOB_NEGATE)?(!status):status); + } else { + if (p->next == 0) { + proc_set_last_status(job_get_flag(j, JOB_NEGATE) ? (!status) : status); } p->completed = 1; } block_output_io_buffer.reset(); break; - } - case INTERNAL_BUILTIN: - { - /* - 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 when - possible. - */ - + case INTERNAL_BUILTIN: { + // 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 when possible. bool fork_was_skipped = false; - const shared_ptr stdout_io = process_net_io_chain.get_io_for_fd(STDOUT_FILENO); - const shared_ptr stderr_io = process_net_io_chain.get_io_for_fd(STDERR_FILENO); - + const shared_ptr stdout_io = + process_net_io_chain.get_io_for_fd(STDOUT_FILENO); + const shared_ptr stderr_io = + process_net_io_chain.get_io_for_fd(STDERR_FILENO); + assert(builtin_io_streams.get() != NULL); const wcstring &stdout_buffer = builtin_io_streams->out.buffer(); const wcstring &stderr_buffer = builtin_io_streams->err.buffer(); - /* If we are outputting to a file, we have to actually do it, even if we have no output, so that we can truncate the file. Does not apply to /dev/null. */ - bool must_fork = redirection_is_to_real_file(stdout_io.get()) || redirection_is_to_real_file(stderr_io.get()); - if (! must_fork) - { - if (p->next == NULL) - { - const bool stdout_is_to_buffer = stdout_io && stdout_io->io_mode == IO_BUFFER; - const bool no_stdout_output = stdout_buffer.empty(); - const bool no_stderr_output = stderr_buffer.empty(); + // If we are outputting to a file, we have to actually do it, even if we have no + // output, so that we can truncate the file. Does not apply to /dev/null. + bool must_fork = redirection_is_to_real_file(stdout_io.get()) || + redirection_is_to_real_file(stderr_io.get()); + if (!must_fork) { + if (p->next == NULL) { + const bool stdout_is_to_buffer = + stdout_io && stdout_io->io_mode == IO_BUFFER; + const bool no_stdout_output = stdout_buffer.empty(); + const bool no_stderr_output = stderr_buffer.empty(); - if (no_stdout_output && no_stderr_output) - { - /* The builtin produced no output and is not inside of a pipeline. No need to fork or even output anything. */ - if (g_log_forks) - { - // This one is very wordy - //printf("fork #-: Skipping fork due to no output for internal builtin for '%ls'\n", p->argv0()); + if (no_stdout_output && no_stderr_output) { + // The builtin produced no output and is not inside of a pipeline. No + // need to fork or even output anything. + if (g_log_forks) { + // This one is very wordy. + // printf("fork #-: Skipping fork due to no output for internal + // builtin for '%ls'\n", p->argv0()); } fork_was_skipped = true; - } - else if (no_stderr_output && stdout_is_to_buffer) - { - /* The builtin produced no stderr, and its stdout is going to an internal buffer. There is no need to fork. This helps out the performance quite a bit in complex completion code. */ - if (g_log_forks) - { - printf("fork #-: Skipping fork due to buffered output for internal builtin for '%ls'\n", p->argv0()); + } else if (no_stderr_output && stdout_is_to_buffer) { + // The builtin produced no stderr, and its stdout is going to an + // internal buffer. There is no need to fork. This helps out the + // performance quite a bit in complex completion code. + if (g_log_forks) { + printf( + "fork #-: Skipping fork due to buffered output for internal " + "builtin for '%ls'\n", + p->argv0()); } CAST_INIT(io_buffer_t *, io_buffer, stdout_io.get()); const std::string res = wcs2string(builtin_io_streams->out.buffer()); io_buffer->out_buffer_append(res.data(), res.size()); fork_was_skipped = true; - } - else if (stdout_io.get() == NULL && stderr_io.get() == NULL) - { - /* We are writing to normal stdout and stderr. Just do it - no need to fork. */ - if (g_log_forks) - { - printf("fork #-: Skipping fork due to ordinary output for internal builtin for '%ls'\n", p->argv0()); + } else if (stdout_io.get() == NULL && stderr_io.get() == NULL) { + // We are writing to normal stdout and stderr. Just do it - no need to + // fork. + if (g_log_forks) { + printf( + "fork #-: Skipping fork due to ordinary output for internal " + "builtin for '%ls'\n", + p->argv0()); } const std::string outbuff = wcs2string(stdout_buffer); const std::string errbuff = wcs2string(stderr_buffer); - bool builtin_io_done = do_builtin_io(outbuff.data(), outbuff.size(), errbuff.data(), errbuff.size()); - if (! builtin_io_done && errno != EPIPE) - { + bool builtin_io_done = do_builtin_io(outbuff.data(), outbuff.size(), + errbuff.data(), errbuff.size()); + if (!builtin_io_done && errno != EPIPE) { show_stackframe(); } fork_was_skipped = true; @@ -1191,25 +971,22 @@ void exec_job(parser_t &parser, job_t *j) } } - - if (fork_was_skipped) - { - p->completed=1; - if (p->next == 0) - { - debug(3, L"Set status of %ls to %d using short circuit", j->command_wcstr(), p->status); + if (fork_was_skipped) { + p->completed = 1; + if (p->next == 0) { + debug(3, L"Set status of %ls to %d using short circuit", j->command_wcstr(), + p->status); int status = p->status; - proc_set_last_status(job_get_flag(j, JOB_NEGATE)?(!status):status); + proc_set_last_status(job_get_flag(j, JOB_NEGATE) ? (!status) : status); } - } - else - { - - - /* Ok, unfortunately, 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. */ - - /* These strings may contain embedded nulls, so don't treat them as C strings */ + } else { + // Ok, unfortunately, 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. + // + // These strings may contain embedded nulls, so don't treat them as C strings. const std::string outbuff_str = wcs2string(stdout_buffer); const char *outbuff = outbuff_str.data(); size_t outbuff_len = outbuff_str.size(); @@ -1220,30 +997,21 @@ void exec_job(parser_t &parser, job_t *j) fflush(stdout); fflush(stderr); - if (g_log_forks) - { - printf("fork #%d: Executing fork for internal builtin for '%ls'\n", g_fork_count, p->argv0()); + if (g_log_forks) { + printf("fork #%d: Executing fork for internal builtin for '%ls'\n", + g_fork_count, p->argv0()); } pid = execute_fork(false); - if (pid == 0) - { - /* - This is the child process. Setup redirections, - print correct output to stdout and stderr, and - then exit. - */ + 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, process_net_io_chain); do_builtin_io(outbuff, outbuff_len, errbuff, errbuff_len); exit_without_destructors(p->status); - } - else - { - /* - This is the parent process. Store away - information on the child, and possibly give - it control over the terminal. - */ + } 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); @@ -1253,93 +1021,87 @@ void exec_job(parser_t &parser, job_t *j) break; } - case EXTERNAL: - { - /* Get argv and envv before we fork */ + case EXTERNAL: { + // Get argv and envv before we fork. null_terminated_array_t argv_array; convert_wide_array_to_narrow(p->get_argv_array(), &argv_array); - /* Ensure that stdin is blocking before we hand it off (see issue #176). It's a little strange that we only do this with stdin and not with stdout or stderr. However in practice, setting or clearing O_NONBLOCK on stdin also sets it for the other two fds, presumably because they refer to the same underlying file (/dev/tty?) */ + // Ensure that stdin is blocking before we hand it off (see issue #176). It's a + // little strange that we only do this with stdin and not with stdout or stderr. + // However in practice, setting or clearing O_NONBLOCK on stdin also sets it for the + // other two fds, presumably because they refer to the same underlying file + // (/dev/tty?). make_fd_blocking(STDIN_FILENO); - const char * const *argv = argv_array.get(); - const char * const *envv = env_export_arr(false); + const char *const *argv = argv_array.get(); + const char *const *envv = env_export_arr(false); std::string actual_cmd_str = wcs2string(p->actual_cmd); const char *actual_cmd = actual_cmd_str.c_str(); - + const wchar_t *reader_current_filename(void); - if (g_log_forks) - { + if (g_log_forks) { const wchar_t *file = reader_current_filename(); - printf("fork #%d: forking for '%s' in '%ls'\n", g_fork_count, actual_cmd, file ? file : L""); + printf("fork #%d: forking for '%s' in '%ls'\n", g_fork_count, actual_cmd, + file ? file : L""); } #if FISH_USE_POSIX_SPAWN - /* Prefer to use posix_spawn, since it's faster on some systems like OS X */ + // 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 */ + 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, process_net_io_chain); - if (made_it) - { - /* We successfully made the attributes and actions; actually call posix_spawn */ - int spawn_ret = posix_spawn(&pid, actual_cmd, &actions, &attr, const_cast(argv), const_cast(envv)); + bool made_it = fork_actions_make_spawn_properties(&attr, &actions, j, p, + process_net_io_chain); + if (made_it) { + // We successfully made the attributes and actions; actually call + // posix_spawn. + int spawn_ret = posix_spawn(&pid, actual_cmd, &actions, &attr, + const_cast(argv), + const_cast(envv)); - /* This usleep can be used to test for various race conditions (https://github.com/fish-shell/fish-shell/issues/360) */ - //usleep(10000); + // 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) - { + if (spawn_ret != 0) { safe_report_exec_error(spawn_ret, actual_cmd, argv, envv); - /* Make sure our pid isn't set */ + // Make sure our pid isn't set. pid = 0; } - /* Clean up our actions */ + // 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) - { + // 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 + } else #endif { pid = execute_fork(false); - if (pid == 0) - { - /* This is the child process. */ + if (pid == 0) { + // This is the child process. p->pid = getpid(); setup_child_process(j, p, process_net_io_chain); safe_launch_process(p, actual_cmd, argv, envv); - - /* - safe_launch_process _never_ returns... - */ + // safe_launch_process _never_ returns... assert(0 && "safe_launch_process should not have returned"); - } - else if (pid < 0) - { + } else if (pid < 0) { job_mark_process_as_failed(j, p); exec_error = true; } } - - /* - This is the parent process. Store away - information on the child, and possibly fice - it control over the terminal. - */ + // 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); @@ -1347,141 +1109,115 @@ void exec_job(parser_t &parser, job_t *j) break; } - case INTERNAL_EXEC: - { - /* We should have handled exec up above */ - assert(0 && "INTERNAL_EXEC process found in pipeline, where it should never be. Aborting."); + case INTERNAL_EXEC: { + // We should have handled exec up above. + assert( + 0 && + "INTERNAL_EXEC process found in pipeline, where it should never be. Aborting."); break; } } - /* - Close the pipe the current process uses to read from the - previous process_t - */ - if (pipe_current_read >= 0) - { + // Close the pipe the current process uses to read from the previous process_t. + if (pipe_current_read >= 0) { exec_close(pipe_current_read); pipe_current_read = -1; } - /* Close the write end too, since the curent child subprocess already has a copy of it. */ - if (pipe_current_write >= 0) - { + // Close the write end too, since the curent child subprocess already has a copy of it. + if (pipe_current_write >= 0) { exec_close(pipe_current_write); pipe_current_write = -1; } } - /* Clean up any file descriptors we left open */ - if (pipe_current_read >= 0) - exec_close(pipe_current_read); - if (pipe_current_write >= 0) - exec_close(pipe_current_write); - if (pipe_next_read >= 0) - exec_close(pipe_next_read); + // Clean up any file descriptors we left open. + if (pipe_current_read >= 0) exec_close(pipe_current_read); + if (pipe_current_write >= 0) exec_close(pipe_current_write); + if (pipe_next_read >= 0) exec_close(pipe_next_read); - /* The keepalive process is no longer needed, so we terminate it with extreme prejudice */ - if (needs_keepalive) - { + // The keepalive process is no longer needed, so we terminate it with extreme prejudice. + if (needs_keepalive) { kill(keepalive.pid, SIGKILL); } signal_unblock(); - debug(3, L"Job is constructed"); job_set_flag(j, JOB_CONSTRUCTED, 1); - - if (!job_get_flag(j, JOB_FOREGROUND)) - { + if (!job_get_flag(j, JOB_FOREGROUND)) { proc_last_bg_pid = j->pgid; } - if (! exec_error) - { + if (!exec_error) { job_continue(j, false); - } - else - { - /* Mark the errored job as not in the foreground. I can't fully justify whether this is the right place, but it prevents sanity_lose from complaining. */ + } else { + // Mark the errored job as not in the foreground. I can't fully justify whether this is the + // right place, but it prevents sanity_lose from complaining. job_set_flag(j, JOB_FOREGROUND, 0); } - } - -static int exec_subshell_internal(const wcstring &cmd, wcstring_list_t *lst, bool apply_exit_status) -{ +static int exec_subshell_internal(const wcstring &cmd, wcstring_list_t *lst, + bool apply_exit_status) { ASSERT_IS_MAIN_THREAD(); int prev_subshell = is_subshell; const int prev_status = proc_get_last_status(); - bool split_output=false; + bool split_output = false; - //fprintf(stderr, "subcmd %ls\n", cmd.c_str()); + // fprintf(stderr, "subcmd %ls\n", cmd.c_str()); const env_var_t ifs = env_get_string(L"IFS"); - if (! ifs.missing_or_empty()) - { - split_output=true; + if (!ifs.missing_or_empty()) { + split_output = true; } - is_subshell=1; + is_subshell = 1; + int subcommand_status = -1; // assume the worst - int subcommand_status = -1; //assume the worst - - // IO buffer creation may fail (e.g. if we have too many open files to make a pipe), so this may be null + // IO buffer creation may fail (e.g. if we have too many open files to make a pipe), so this may + // be null. const shared_ptr io_buffer(io_buffer_t::create(STDOUT_FILENO, io_chain_t())); - if (io_buffer.get() != NULL) - { + if (io_buffer.get() != NULL) { parser_t &parser = parser_t::principal_parser(); - if (parser.eval(cmd, io_chain_t(io_buffer), SUBST) == 0) - { + if (parser.eval(cmd, io_chain_t(io_buffer), SUBST) == 0) { subcommand_status = proc_get_last_status(); } io_buffer->read(); } - // If the caller asked us to preserve the exit status, restore the old status - // Otherwise set the status of the subcommand + // If the caller asked us to preserve the exit status, restore the old status. Otherwise set the + // status of the subcommand. proc_set_last_status(apply_exit_status ? subcommand_status : prev_status); - is_subshell = prev_subshell; - if (lst != NULL && io_buffer.get() != NULL) - { + if (lst != NULL && io_buffer.get() != NULL) { const char *begin = io_buffer->out_buffer_ptr(); const char *end = begin + io_buffer->out_buffer_size(); - if (split_output) - { + if (split_output) { const char *cursor = begin; - while (cursor < end) - { - // Look for the next separator + while (cursor < end) { + // Look for the next separator. const char *stop = (const char *)memchr(cursor, '\n', end - cursor); const bool hit_separator = (stop != NULL); - if (! hit_separator) - { - // If it's not found, just use the end + if (!hit_separator) { + // If it's not found, just use the end. stop = end; } - // Stop now points at the first character we do not want to copy + // Stop now points at the first character we do not want to copy. const wcstring wc = str2wcstring(cursor, stop - cursor); lst->push_back(wc); - // If we hit a separator, skip over it; otherwise we're at the end + // If we hit a separator, skip over it; otherwise we're at the end. cursor = stop + (hit_separator ? 1 : 0); } - } - else - { - // we're not splitting output, but we still want to trim off a trailing newline - if (end != begin && end[-1] == '\n') - { + } else { + // we're not splitting output, but we still want to trim off a trailing newline. + if (end != begin && end[-1] == '\n') { --end; } const wcstring wc = str2wcstring(begin, end - begin); @@ -1492,14 +1228,12 @@ static int exec_subshell_internal(const wcstring &cmd, wcstring_list_t *lst, boo return subcommand_status; } -int exec_subshell(const wcstring &cmd, std::vector &outputs, bool apply_exit_status) -{ +int exec_subshell(const wcstring &cmd, std::vector &outputs, bool apply_exit_status) { ASSERT_IS_MAIN_THREAD(); return exec_subshell_internal(cmd, &outputs, apply_exit_status); } -int exec_subshell(const wcstring &cmd, bool apply_exit_status) -{ +int exec_subshell(const wcstring &cmd, bool apply_exit_status) { ASSERT_IS_MAIN_THREAD(); return exec_subshell_internal(cmd, NULL, apply_exit_status); } diff --git a/src/exec.h b/src/exec.h index f2d6c8e7b..1c402a6f5 100644 --- a/src/exec.h +++ b/src/exec.h @@ -1,73 +1,51 @@ -/** \file exec.h - Prototypes for functions for executing a program -*/ - +// Prototypes for functions for executing a program. #ifndef FISH_EXEC_H #define FISH_EXEC_H +#include #include #include -#include #include "common.h" -/** - pipe redirection error message -*/ +/// Pipe redirection error message. #define PIPE_ERROR _(L"An error occurred while setting up pipe") -/** - Execute the processes specified by j. - - I've put a fair bit of work into making builtins behave like other - programs as far as pipes are concerned. Unlike i.e. bash, builtins - can pipe to other builtins with arbitrary amounts of data, and so - on. To do this, after a builtin is run in the real process, it - forks and a dummy process is created, responsible for writing the - output of the builtin. This is surprisingly cheap on my computer, - probably because of the marvels of copy on write forking. - - This rule is short circuited in the case where a builtin does not - output to a pipe and does in fact not output anything. The speed - improvement from this optimization is not noticable on a normal - computer/OS in regular use, but the promiscous amounts of forking - that resulted was responsible for a huge slowdown when using - Valgrind as well as when doing complex command-specific - completions. - - -*/ +/// Execute the processes specified by j. +/// +/// I've put a fair bit of work into making builtins behave like other programs as far as pipes are +/// concerned. Unlike i.e. bash, builtins can pipe to other builtins with arbitrary amounts of data, +/// and so on. To do this, after a builtin is run in the real process, it forks and a dummy process +/// is created, responsible for writing the output of the builtin. This is surprisingly cheap on my +/// computer, probably because of the marvels of copy on write forking. +/// +/// This rule is short circuited in the case where a builtin does not output to a pipe and does in +/// fact not output anything. The speed improvement from this optimization is not noticable on a +/// normal computer/OS in regular use, but the promiscous amounts of forking that resulted was +/// responsible for a huge slowdown when using Valgrind as well as when doing complex +/// command-specific completions. class job_t; class parser_t; void exec_job(parser_t &parser, job_t *j); -/** - Evaluate the expression cmd in a subshell, add the outputs into the - list l. On return, the status flag as returned bu \c - proc_gfet_last_status will not be changed. - - \param cmd the command to execute - \param outputs The list to insert output into. - - \return the status of the last job to exit, or -1 if en error was encountered. -*/ +/// Evaluate the expression cmd in a subshell, add the outputs into the list l. On return, the +/// status flag as returned bu \c proc_gfet_last_status will not be changed. +/// +/// \param cmd the command to execute +/// \param outputs The list to insert output into. +/// +/// \return the status of the last job to exit, or -1 if en error was encountered. int exec_subshell(const wcstring &cmd, std::vector &outputs, bool preserve_exit_status); int exec_subshell(const wcstring &cmd, bool preserve_exit_status); - -/** - Loops over close until the syscall was run without being - interrupted. -*/ +/// Loops over close until the syscall was run without being interrupted. void exec_close(int fd); -/** - Call pipe(), and add resulting fds to open_fds, the list of opened - file descriptors for pipes. The pipes are marked CLO_EXEC. -*/ +/// Call pipe(), and add resulting fds to open_fds, the list of opened file descriptors for pipes. +/// The pipes are marked CLO_EXEC. int exec_pipe(int fd[2]); -/** Gets the interpreter for a given command */ +/// Gets the interpreter for a given command. char *get_interpreter(const char *command, char *interpreter, size_t buff_size); #endif From ffad7b0b299c3eeb090f628e846113e9bb939ff1 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Thu, 28 Apr 2016 20:10:27 -0700 Subject: [PATCH 158/363] restyle expand module to match project style Reduces lint errors from 183 to 126 (-31%). Line count from 2231 to 1787 (-20%). Another step in resolving issue #2902. --- src/expand.cpp | 1790 ++++++++++++++++++------------------------------ src/expand.h | 194 +++--- 2 files changed, 770 insertions(+), 1214 deletions(-) diff --git a/src/expand.cpp b/src/expand.cpp index 1ac626e96..d64b3cbcc 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -1,21 +1,16 @@ -/**\file expand.c - -String expansion functions. These functions perform several kinds of -parameter expansion. - -*/ +// String expansion functions. These functions perform several kinds of parameter expansion. // IWYU pragma: no_include #include "config.h" -#include -#include -#include -#include -#include -#include #include #include +#include +#include +#include +#include #include +#include +#include #include #ifdef HAVE_SYS_SYSCTL_H #include // IWYU pragma: keep @@ -25,116 +20,87 @@ parameter expansion. #ifdef SunOS #include #endif -#include // IWYU pragma: keep #include +#include // IWYU pragma: keep #if __APPLE__ #include #else -#include #include +#include #endif -#include "fallback.h" // IWYU pragma: keep -#include "util.h" #include "common.h" -#include "wutil.h" // IWYU pragma: keep -#include "env.h" -#include "proc.h" -#include "path.h" -#include "expand.h" -#include "wildcard.h" -#include "exec.h" #include "complete.h" +#include "env.h" +#include "exec.h" +#include "expand.h" +#include "fallback.h" // IWYU pragma: keep #include "iothread.h" -#include "parse_util.h" #include "parse_constants.h" +#include "parse_util.h" +#include "path.h" +#include "proc.h" +#include "util.h" +#include "wildcard.h" +#include "wutil.h" // IWYU pragma: keep #ifdef KERN_PROCARGS2 #else #include "tokenizer.h" #endif -/** - Description for child process -*/ -#define COMPLETE_CHILD_PROCESS_DESC _( L"Child process") +/// Description for child process. +#define COMPLETE_CHILD_PROCESS_DESC _(L"Child process") -/** - Description for non-child process -*/ -#define COMPLETE_PROCESS_DESC _( L"Process") +/// Description for non-child process. +#define COMPLETE_PROCESS_DESC _(L"Process") -/** - Description for long job -*/ -#define COMPLETE_JOB_DESC _( L"Job") +/// Description for long job. +#define COMPLETE_JOB_DESC _(L"Job") -/** - Description for short job. The job command is concatenated -*/ -#define COMPLETE_JOB_DESC_VAL _( L"Job: %ls") +/// Description for short job. The job command is concatenated. +#define COMPLETE_JOB_DESC_VAL _(L"Job: %ls") -/** - Description for the shells own pid -*/ -#define COMPLETE_SELF_DESC _( L"Shell process") +/// Description for the shells own pid. +#define COMPLETE_SELF_DESC _(L"Shell process") -/** - Description for the shells own pid -*/ -#define COMPLETE_LAST_DESC _( L"Last background job") +/// Description for the shells own pid. +#define COMPLETE_LAST_DESC _(L"Last background job") -/** - String in process expansion denoting ourself -*/ +/// String in process expansion denoting ourself. #define SELF_STR L"self" -/** - String in process expansion denoting last background job -*/ +/// String in process expansion denoting last background job. #define LAST_STR L"last" -/** - Characters which make a string unclean if they are the first - character of the string. See \c expand_is_clean(). -*/ +/// Characters which make a string unclean if they are the first character of the string. See \c +/// expand_is_clean(). #define UNCLEAN_FIRST L"~%" -/** - Unclean characters. See \c expand_is_clean(). -*/ +/// Unclean characters. See \c expand_is_clean(). #define UNCLEAN L"$*?\\\"'({})" static void remove_internal_separator(wcstring *s, bool conv); -/** - Test if the specified argument is clean, i.e. it does not contain - any tokens which need to be expanded or otherwise altered. Clean - strings can be passed through expand_string and expand_one without - changing them. About two thirds of all strings are clean, so - skipping expansion on them actually does save a small amount of - time, since it avoids multiple memory allocations during the - expansion process. - - \param in the string to test - */ -static bool expand_is_clean(const wcstring &in) -{ - if (in.empty()) - return true; - - /* Test characters that have a special meaning in the first character position */ - if (wcschr(UNCLEAN_FIRST, in.at(0)) != NULL) - return false; +/// Test if the specified argument is clean, i.e. it does not contain any tokens which need to be +/// expanded or otherwise altered. Clean strings can be passed through expand_string and expand_one +/// without changing them. About two thirds of all strings are clean, so skipping expansion on them +/// actually does save a small amount of time, since it avoids multiple memory allocations during +/// the expansion process. +/// +/// \param in the string to test +static bool expand_is_clean(const wcstring &in) { + if (in.empty()) return true; - /* Test characters that have a special meaning in any character position */ + // Test characters that have a special meaning in the first character position. + if (wcschr(UNCLEAN_FIRST, in.at(0)) != NULL) return false; + + // Test characters that have a special meaning in any character position. return in.find_first_of(UNCLEAN) == wcstring::npos; } - -/* Append a syntax error to the given error list */ -static void append_syntax_error(parse_error_list_t *errors, size_t source_start, const wchar_t *fmt, ...) -{ - if (errors != NULL) - { +/// Append a syntax error to the given error list. +static void append_syntax_error(parse_error_list_t *errors, size_t source_start, const wchar_t *fmt, + ...) { + if (errors != NULL) { parse_error_t error; error.source_start = source_start; error.source_length = 0; @@ -149,11 +115,10 @@ static void append_syntax_error(parse_error_list_t *errors, size_t source_start, } } -/* Append a cmdsub error to the given error list */ -static void append_cmdsub_error(parse_error_list_t *errors, size_t source_start, const wchar_t *fmt, ...) -{ - if (errors != NULL) - { +/// Append a cmdsub error to the given error list. +static void append_cmdsub_error(parse_error_list_t *errors, size_t source_start, const wchar_t *fmt, + ...) { + if (errors != NULL) { parse_error_t error; error.source_start = source_start; error.source_length = 0; @@ -168,93 +133,66 @@ static void append_cmdsub_error(parse_error_list_t *errors, size_t source_start, } } - -/** - Return the environment variable value for the string starting at \c in. -*/ -static env_var_t expand_var(const wchar_t *in) -{ - if (!in) - return env_var_t::missing_var(); +/// Return the environment variable value for the string starting at \c in. +static env_var_t expand_var(const wchar_t *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) -{ - switch (*str) - { - case 0: +/// 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) { + switch (*str) { + case 0: { return 1; - + } case L'\n': case L'\t': case L'\r': case L'\b': - case L'\x1b': + case L'\x1b': { return 0; - - default: - return is_quotable(str+1); + } + default: { return is_quotable(str + 1); } } return 0; - } -static int is_quotable(const wcstring &str) -{ - return is_quotable(str.c_str()); -} - -wcstring expand_escape_variable(const wcstring &in) -{ +static int is_quotable(const wcstring &str) { return is_quotable(str.c_str()); } +wcstring expand_escape_variable(const wcstring &in) { wcstring_list_t lst; wcstring buff; tokenize_variable_array(in, lst); - switch (lst.size()) - { - case 0: + switch (lst.size()) { + case 0: { buff.append(L"''"); break; - - case 1: - { + } + case 1: { const wcstring &el = lst.at(0); - if (el.find(L' ') != wcstring::npos && is_quotable(el)) - { + if (el.find(L' ') != wcstring::npos && is_quotable(el)) { buff.append(L"'"); buff.append(el); buff.append(L"'"); - } - else - { + } else { buff.append(escape_string(el, 1)); } break; } - default: - { - for (size_t j=0; j L'9') - { +/// Tests if all characters in the wide string are numeric. +static int iswnumeric(const wchar_t *n) { + for (; *n; n++) { + if (*n < L'0' || *n > L'9') { return 0; } } 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) -{ - /* 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; +/// 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) { + // 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. */ + // Get the command to match against. We're only interested in the last path component. const wcstring base_cmd = wbasename(cmd); bool result = string_prefixes_string(proc, base_cmd); - if (result) - { - /* It's a match. Return the offset within the full command. */ - if (offset) - *offset = cmd.size() - base_cmd.size(); + if (result) { + // It's a match. Return the offset within the full command. + if (offset) *offset = cmd.size() - base_cmd.size(); } return result; } -/** Helper class for iterating over processes. The names returned have been unescaped (e.g. may include spaces) */ +/// Helper class for iterating over processes. The names returned have been unescaped (e.g. may +/// include spaces). #ifdef KERN_PROCARGS2 -/* BSD / OS X process completions */ +// 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); }; -wcstring process_iterator_t::name_for_pid(pid_t pid) -{ +wcstring process_iterator_t::name_for_pid(pid_t pid) { wcstring result; int mib[4], maxarg = 0, numArgs = 0; size_t size = 0; @@ -336,14 +259,12 @@ 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) - { + if (args == NULL) { return result; } @@ -352,10 +273,9 @@ 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) - { + if (sysctl(mib, 3, args, &size, NULL, 0) == -1) { free(args); - return result;; + return result; } memcpy(&numArgs, args, sizeof(numArgs)); @@ -365,194 +285,146 @@ wcstring process_iterator_t::name_for_pid(pid_t pid) return result; } -bool process_iterator_t::next_process(wcstring *out_str, pid_t *out_pid) -{ +bool process_iterator_t::next_process(wcstring *out_str, pid_t *out_pid) { wcstring name; pid_t pid = 0; bool result = false; - while (idx < pids.size()) - { + while (idx < pids.size()) { pid = pids.at(idx++); name = name_for_pid(pid); - if (! name.empty()) - { + if (!name.empty()) { result = true; break; } } - if (result) - { + if (result) { *out_str = name; *out_pid = pid; } return result; } -process_iterator_t::process_iterator_t() : idx(0) -{ - int err; - struct kinfo_proc * result; - bool done; - static const int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 }; - // Declaring name as const requires us to cast it when passing it to - // sysctl because the prototype doesn't include the const modifier. - size_t length; - - - // We start by calling sysctl with result == NULL and length == 0. - // That will succeed, and set length to the appropriate length. - // We then allocate a buffer of that size and call sysctl again - // with that buffer. If that succeeds, we're done. If that fails - // with ENOMEM, we have to throw away our buffer and loop. Note - // that the loop causes use to call sysctl with NULL again; this - // is necessary because the ENOMEM failure case sets length to - // the amount of data returned, not the amount of data that - // could have been returned. +process_iterator_t::process_iterator_t() : idx(0) { + int err; + struct kinfo_proc *result; + bool done; + static const int name[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0}; + // Declaring name as const requires us to cast it when passing it to sysctl because the + // prototype doesn't include the const modifier. + size_t length; + // We start by calling sysctl with result == NULL and length == 0. That will succeed, and set + // length to the appropriate length. We then allocate a buffer of that size and call sysctl + // again with that buffer. If that succeeds, we're done. If that fails with ENOMEM, we have to + // throw away our buffer and loop. Note that the loop causes use to call sysctl with NULL + // again; this is necessary because the ENOMEM failure case sets length to the amount of data + // returned, not the amount of data that could have been returned. result = NULL; done = false; - do - { + do { assert(result == NULL); // Call sysctl with a NULL buffer. - length = 0; - err = sysctl((int *) name, (sizeof(name) / sizeof(*name)) - 1, - NULL, &length, - NULL, 0); - if (err == -1) - { + err = sysctl((int *)name, (sizeof(name) / sizeof(*name)) - 1, NULL, &length, NULL, 0); + if (err == -1) { err = errno; } - // Allocate an appropriately sized buffer based on the results - // from the previous call. - - if (err == 0) - { + // Allocate an appropriately sized buffer based on the results from the previous call. + if (err == 0) { result = (struct kinfo_proc *)malloc(length); - if (result == NULL) - { + if (result == NULL) { err = ENOMEM; } } - // Call sysctl again with the new buffer. If we get an ENOMEM - // error, toss away our buffer and start again. - - if (err == 0) - { - err = sysctl((int *) name, (sizeof(name) / sizeof(*name)) - 1, - result, &length, - NULL, 0); - if (err == -1) - { + // Call sysctl again with the new buffer. If we get an ENOMEM error, toss away our buffer + // and start again. + if (err == 0) { + err = sysctl((int *)name, (sizeof(name) / sizeof(*name)) - 1, result, &length, NULL, 0); + 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) - { + if (err == 0 && result != NULL) { for (size_t idx = 0; idx < length / sizeof(struct kinfo_proc); idx++) pids.push_back(result[idx].kp_proc.p_pid); } - if (result) - free(result); + if (result) free(result); } #else -/* /proc style process completions */ -class process_iterator_t -{ +/// /proc style process completions. +class process_iterator_t { DIR *dir; -public: + public: process_iterator_t(); ~process_iterator_t(); bool next_process(wcstring *out_str, pid_t *out_pid); }; -process_iterator_t::process_iterator_t(void) -{ - dir = opendir("/proc"); +process_iterator_t::process_iterator_t(void) { dir = opendir("/proc"); } + +process_iterator_t::~process_iterator_t(void) { + if (dir) closedir(dir); } -process_iterator_t::~process_iterator_t(void) -{ - if (dir) - closedir(dir); -} - -bool process_iterator_t::next_process(wcstring *out_str, pid_t *out_pid) -{ +bool process_iterator_t::next_process(wcstring *out_str, pid_t *out_pid) { wcstring cmd; pid_t pid = 0; - while (cmd.empty()) - { + while (cmd.empty()) { wcstring name; - if (! dir || ! wreaddir(dir, name)) - break; - - if (!iswnumeric(name.c_str())) - continue; + if (!dir || !wreaddir(dir, name)) break; + 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 */ + // Remember the pid. pid = fish_wcstoi(name.c_str(), NULL, 10); - /* the 'cmdline' file exists, it should contain the commandline */ + // The 'cmdline' file exists, it should contain the commandline. FILE *cmdfile; - if ((cmdfile=wfopen(path + L"/cmdline", "r"))) - { + if ((cmdfile = wfopen(path + L"/cmdline", "r"))) { wcstring full_command_line; fgetws2(&full_command_line, cmdfile); - /* The command line needs to be escaped */ + // The command line needs to be escaped. cmd = tok_first(full_command_line); } #ifdef SunOS - else if ((cmdfile=wfopen(path + L"/psinfo", "r"))) - { + else if ((cmdfile = wfopen(path + L"/psinfo", "r"))) { psinfo_t info; - if (fread(&info, sizeof(info), 1, cmdfile)) - { - /* The filename is unescaped */ + if (fread(&info, sizeof(info), 1, cmdfile)) { + // The filename is unescaped. cmd = str2wcstring(info.pr_fname); } } #endif - if (cmdfile) - fclose(cmdfile); + if (cmdfile) fclose(cmdfile); } - bool result = ! cmd.empty(); - if (result) - { + bool result = !cmd.empty(); + if (result) { *out_str = cmd; *out_pid = pid; } @@ -561,171 +433,118 @@ bool process_iterator_t::next_process(wcstring *out_str, pid_t *out_pid) #endif -std::vector expand_get_all_process_names(void) -{ +std::vector expand_get_all_process_names(void) { wcstring name; pid_t pid; process_iterator_t iterator; std::vector result; - while (iterator.next_process(&name, &pid)) - { + while (iterator.next_process(&name, &pid)) { result.push_back(name); } return result; } -/* Helper function to do a job search. */ -struct find_job_data_t -{ - const wchar_t *proc; /* The process to search for - possibly numeric, possibly a name */ +// Helper function to do a job search. +struct find_job_data_t { + const wchar_t *proc; // the process to search for - possibly numeric, possibly a name expand_flags_t flags; std::vector *completions; }; -/* The following function is invoked on the main thread, because the job list is not thread safe. It should search the job list for something matching the given proc, and then return 1 to stop the search, 0 to continue it */ -static int find_job(const struct find_job_data_t *info) -{ +/// The following function is invoked on the main thread, because the job list is not thread safe. +/// It should search the job list for something matching the given proc, and then return 1 to stop +/// the search, 0 to continue it . +static int find_job(const struct find_job_data_t *info) { ASSERT_IS_MAIN_THREAD(); - const wchar_t * const proc = info->proc; + const wchar_t *const proc = info->proc; const expand_flags_t flags = info->flags; std::vector &completions = *info->completions; const job_t *j; int found = 0; - // If we are not doing tab completion, we first check for the single '%' - // character, because an empty string will pass the numeric check below. - // But if we are doing tab completion, we want all of the job IDs as - // completion options, not just the last job backgrounded, so we pass this + // If we are not doing tab completion, we first check for the single '%' character, because an + // empty string will pass the numeric check below. But if we are doing tab completion, we want + // all of the job IDs as completion options, not just the last job backgrounded, so we pass this // first block in favor of the second. - if (wcslen(proc)==0 && !(flags & EXPAND_FOR_COMPLETIONS)) - { - /* - This is an empty job expansion: '%' - It expands to the last job backgrounded. - */ + if (wcslen(proc) == 0 && !(flags & EXPAND_FOR_COMPLETIONS)) { + // This is an empty job expansion: '%'. It expands to the last job backgrounded. job_iterator_t jobs; - while ((j = jobs.next())) - { - if (!j->command_is_empty()) - { + while ((j = jobs.next())) { + if (!j->command_is_empty()) { append_completion(&completions, to_string(j->pgid)); break; } } - /* - You don't *really* want to flip a coin between killing - the last process backgrounded and all processes, do you? - Let's not try other match methods with the solo '%' syntax. - */ + // You don't *really* want to flip a coin between killing the last process backgrounded and + // all processes, do you? Let's not try other match methods with the solo '%' syntax. found = 1; - } - else if (iswnumeric(proc)) - { - /* - This is a numeric job string, like '%2' - */ - - if (flags & EXPAND_FOR_COMPLETIONS) - { + } else if (iswnumeric(proc)) { + // This is a numeric job string, like '%2'. + if (flags & EXPAND_FOR_COMPLETIONS) { job_iterator_t jobs; - while ((j = jobs.next())) - { + while ((j = jobs.next())) { wchar_t jid[16]; - if (j->command_is_empty()) - continue; + if (j->command_is_empty()) continue; 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(&completions, - jid+wcslen(proc), - desc_buff, - 0); + append_completion(&completions, jid + wcslen(proc), desc_buff, 0); } } - } - else - { + } else { int jid; wchar_t *end; errno = 0; jid = fish_wcstoi(proc, &end, 10); - if (jid > 0 && !errno && !*end) - { + if (jid > 0 && !errno && !*end) { j = job_get(jid); - if ((j != 0) && (j->command_wcstr() != 0) && (!j->command_is_empty())) - { + if ((j != 0) && (j->command_wcstr() != 0) && (!j->command_is_empty())) { append_completion(&completions, to_string(j->pgid)); } } } - /* - Stop here so you can't match a random process name - when you're just trying to use job control. - */ + // Stop here so you can't match a random process name when you're just trying to use job + // control. found = 1; } - if (! found) - { + if (!found) { job_iterator_t jobs; - while ((j = jobs.next())) - { - - if (j->command_is_empty()) - continue; + while ((j = jobs.next())) { + if (j->command_is_empty()) continue; size_t offset; - if (match_pid(j->command(), proc, flags, &offset)) - { - if (flags & EXPAND_FOR_COMPLETIONS) - { - append_completion(&completions, - j->command_wcstr() + offset + wcslen(proc), - COMPLETE_JOB_DESC, - 0); - } - else - { + if (match_pid(j->command(), proc, flags, &offset)) { + if (flags & EXPAND_FOR_COMPLETIONS) { + append_completion(&completions, j->command_wcstr() + offset + wcslen(proc), + COMPLETE_JOB_DESC, 0); + } else { append_completion(&completions, to_string(j->pgid)); found = 1; } } } - if (! found) - { + if (!found) { jobs.reset(); - while ((j = jobs.next())) - { + while ((j = jobs.next())) { process_t *p; - if (j->command_is_empty()) - continue; - for (p=j->first_process; p; p=p->next) - { - if (p->actual_cmd.empty()) - continue; + if (j->command_is_empty()) continue; + for (p = j->first_process; p; p = p->next) { + if (p->actual_cmd.empty()) continue; size_t offset; - if (match_pid(p->actual_cmd, proc, flags, &offset)) - { - if (flags & EXPAND_FOR_COMPLETIONS) - { + if (match_pid(p->actual_cmd, proc, flags, &offset)) { + if (flags & EXPAND_FOR_COMPLETIONS) { append_completion(&completions, wcstring(p->actual_cmd, offset + wcslen(proc)), - COMPLETE_CHILD_PROCESS_DESC, - 0); - } - else - { - append_completion(&completions, - to_string(p->pid), - L"", - 0); + COMPLETE_CHILD_PROCESS_DESC, 0); + } else { + append_completion(&completions, to_string(p->pid), L"", 0); found = 1; } } @@ -737,132 +556,98 @@ static int find_job(const struct find_job_data_t *info) return found; } - -/** - Searches for a job with the specified job id, or a job or process - which has the string \c proc as a prefix of its commandline. Appends - the name of the process as a completion in 'out'. - - If the ACCEPT_INCOMPLETE flag is set, the remaining string for any matches - are inserted. - - Otherwise, any job matching the specified string is matched, and - the job pgid is returned. If no job matches, all child processes - are searched. If no child processes match, and fish can - understand the contents of the /proc filesystem, all the users - processes are searched for matches. -*/ -static void find_process(const wchar_t *proc, expand_flags_t flags, std::vector *out) -{ - if (!(flags & EXPAND_SKIP_JOBS)) - { +/// Searches for a job with the specified job id, or a job or process which has the string \c proc +/// as a prefix of its commandline. Appends the name of the process as a completion in 'out'. +/// +/// If the ACCEPT_INCOMPLETE flag is set, the remaining string for any matches are inserted. +/// +/// Otherwise, any job matching the specified string is matched, and the job pgid is returned. If no +/// job matches, all child processes are searched. If no child processes match, and fish +/// can understand the contents of the /proc filesystem, all the users processes are searched for +/// matches. +static void find_process(const wchar_t *proc, expand_flags_t flags, + std::vector *out) { + if (!(flags & EXPAND_SKIP_JOBS)) { const struct find_job_data_t data = {proc, flags, out}; int found = iothread_perform_on_main(find_job, &data); - if (found) - { + if (found) { return; } } - /* Iterate over all processes */ + // Iterate over all processes. wcstring process_name; pid_t process_pid; process_iterator_t iterator; - while (iterator.next_process(&process_name, &process_pid)) - { + while (iterator.next_process(&process_name, &process_pid)) { size_t offset; - if (match_pid(process_name, proc, flags, &offset)) - { - if (flags & EXPAND_FOR_COMPLETIONS) - { - append_completion(out, - process_name.c_str() + offset + wcslen(proc), - COMPLETE_PROCESS_DESC, - 0); - } - else - { + if (match_pid(process_name, proc, flags, &offset)) { + if (flags & EXPAND_FOR_COMPLETIONS) { + append_completion(out, process_name.c_str() + offset + wcslen(proc), + COMPLETE_PROCESS_DESC, 0); + } else { append_completion(out, to_string(process_pid)); } } } } -/** - Process id expansion -*/ -static bool expand_pid(const wcstring &instr_with_sep, expand_flags_t flags, std::vector *out, parse_error_list_t *errors) -{ - /* Hack. If there's no INTERNAL_SEP and no PROCESS_EXPAND, then there's nothing to do. Check out this "null terminated string." */ +/// Process id expansion. +static bool expand_pid(const wcstring &instr_with_sep, expand_flags_t flags, + std::vector *out, parse_error_list_t *errors) { + // Hack. If there's no INTERNAL_SEP and no PROCESS_EXPAND, then there's nothing to do. Check out + // this "null terminated string." const wchar_t some_chars[] = {INTERNAL_SEPARATOR, PROCESS_EXPAND, L'\0'}; - if (instr_with_sep.find_first_of(some_chars) == wcstring::npos) - { - /* Nothing to do */ + if (instr_with_sep.find_first_of(some_chars) == wcstring::npos) { + // Nothing to do. append_completion(out, instr_with_sep); return true; } - /* expand_string calls us with internal separators in instr...sigh */ + // 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) - { - /* Not a process expansion */ + if (instr.empty() || instr.at(0) != PROCESS_EXPAND) { + // Not a process expansion. append_completion(out, instr); return true; } - const wchar_t * const in = instr.c_str(); + const wchar_t *const in = instr.c_str(); - /* We know we are a process expansion now */ + // We know we are a process expansion now. assert(in[0] == PROCESS_EXPAND); - if (flags & EXPAND_FOR_COMPLETIONS) - { - if (wcsncmp(in+1, SELF_STR, wcslen(in+1))==0) - { - append_completion(out, - &SELF_STR[wcslen(in+1)], - COMPLETE_SELF_DESC, - 0); + if (flags & EXPAND_FOR_COMPLETIONS) { + 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 true; } - if (wcscmp((in+1), LAST_STR)==0) - { - if (proc_last_bg_pid > 0) - { + if (wcscmp((in + 1), LAST_STR) == 0) { + if (proc_last_bg_pid > 0) { append_completion(out, to_string(proc_last_bg_pid)); } return true; } } - /* This is sort of crummy - find_process doesn't return any indication of success, so instead we check to see if it inserted any completions */ + // This is sort of crummy - find_process doesn't return any indication of success, so instead we + // check to see if it inserted any completions. const size_t prev_count = out->size(); - find_process(in+1, flags, out); + find_process(in + 1, flags, out); - if (prev_count == out->size()) - { - if (!(flags & EXPAND_FOR_COMPLETIONS)) - { - /* We failed to find anything */ - append_syntax_error(errors, 1, FAILED_EXPANSION_PROCESS_ERR_MSG, escape(in+1, ESCAPE_NO_QUOTED).c_str()); + if (prev_count == out->size()) { + if (!(flags & EXPAND_FOR_COMPLETIONS)) { + // We failed to find anything. + append_syntax_error(errors, 1, FAILED_EXPANSION_PROCESS_ERR_MSG, + escape(in + 1, ESCAPE_NO_QUOTED).c_str()); return false; } } @@ -870,67 +655,53 @@ static bool expand_pid(const wcstring &instr_with_sep, expand_flags_t flags, std return true; } -/** - Parse an array slicing specification - Returns 0 on success. - If a parse error occurs, returns the index of the bad token. - Note that 0 can never be a bad index because the string always starts with [. - */ -static size_t parse_slice(const wchar_t *in, wchar_t **end_ptr, std::vector &idx, std::vector &source_positions, size_t array_size) -{ +/// Parse an array slicing specification Returns 0 on success. If a parse error occurs, returns the +/// index of the bad token. Note that 0 can never be a bad index because the string always starts +/// with [. +static size_t parse_slice(const wchar_t *in, wchar_t **end_ptr, std::vector &idx, + std::vector &source_positions, size_t array_size) { 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) - { + while (1) { long tmp; - while (iswspace(in[pos]) || (in[pos]==INTERNAL_SEPARATOR)) - pos++; - - if (in[pos] == L']') - { + while (iswspace(in[pos]) || (in[pos] == INTERNAL_SEPARATOR)) pos++; + if (in[pos] == L']') { pos++; break; } - errno=0; + errno = 0; const size_t i1_src_pos = pos; tmp = wcstol(&in[pos], &end, 10); - if ((errno) || (end == &in[pos])) - { + if ((errno) || (end == &in[pos])) { return pos; } - // debug( 0, L"Push idx %d", tmp ); + // 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 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++; const size_t number_start = pos; long tmp1 = wcstol(&in[pos], &end, 10); - if ((errno) || (end == &in[pos])) - { + if ((errno) || (end == &in[pos])) { return pos; } - pos = end-in; + pos = end - in; // debug( 0, L"Push range %d %d", tmp, tmp1 ); - long i2 = tmp1>-1 ? tmp1 : size+tmp1+1; + long i2 = tmp1 > -1 ? tmp1 : size + tmp1 + 1; // debug( 0, L"Push range idx %d %d", i1, i2 ); - short direction = i2 *out, long last_idx, parse_error_list_t *errors) -{ +/// Expand all environment variables in the string *ptr. +/// +/// This function is slow, fragile and complicated. There are lots of little corner cases, like +/// $$foo should do a double expansion, $foo$bar should not double expand bar, etc. Also, it's easy +/// to accidentally leak memory on array out of bounds errors an various other situations. All in +/// all, this function should be rewritten, split out into multiple logical units and carefully +/// tested. After that, it can probably be optimized to do fewer memory allocations, fewer string +/// scans and overall just less work. But until that happens, don't edit it unless you know exactly +/// what you are doing, and do proper testing afterwards. +/// +/// This function operates on strings backwards, starting at last_idx. +/// +/// Note: last_idx is considered to be where it previously finished procesisng. This means it +/// actually starts operating on last_idx-1. As such, to process a string fully, pass string.size() +/// as last_idx instead of string.size()-1. +static int expand_variables(const wcstring &instr, std::vector *out, long last_idx, + parse_error_list_t *errors) { const size_t insize = instr.size(); - // last_idx may be 1 past the end of the string, but no further + // last_idx may be 1 past the end of the string, but no further. assert(last_idx >= 0 && (size_t)last_idx <= insize); - if (last_idx == 0) - { + if (last_idx == 0) { append_completion(out, instr); return true; } @@ -994,48 +756,40 @@ static int expand_variables(const wcstring &instr, std::vector *ou wcstring var_tmp; - // list of indexes + // List of indexes. std::vector var_idx_list; - // parallel array of source positions of each index in the variable list + // Parallel array of source positions of each index in the variable list. std::vector var_pos_list; - // CHECK( out, 0 ); + // CHECK( out, 0 ); - for (long i=last_idx-1; (i>=0) && is_ok && !empty; i--) - { + for (long i = last_idx - 1; (i >= 0) && is_ok && !empty; i--) { const wchar_t c = instr.at(i); - if ((c == VARIABLE_EXPAND) || (c == VARIABLE_EXPAND_SINGLE)) - { - long start_pos = i+1; + 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); + int is_single = (c == VARIABLE_EXPAND_SINGLE); stop_pos = start_pos; - while (stop_pos < insize) - { + while (stop_pos < insize) { const wchar_t nc = instr.at(stop_pos); - if (nc == VARIABLE_EXPAND_EMPTY) - { + if (nc == VARIABLE_EXPAND_EMPTY) { stop_pos++; break; } - if (!wcsvarchr(nc)) - break; + if (!wcsvarchr(nc)) break; stop_pos++; } - /* printf( "Stop for '%c'\n", in[stop_pos]);*/ - + // printf( "Stop for '%c'\n", in[stop_pos]); var_len = stop_pos - start_pos; - if (var_len == 0) - { - if (errors) - { + if (var_len == 0) { + if (errors) { parse_util_expand_variable_error(instr, 0 /* global_token_pos */, i, errors); } @@ -1045,131 +799,100 @@ static int expand_variables(const wcstring &instr, std::vector *ou var_tmp.append(instr, start_pos, var_len); env_var_t var_val; - if (var_len == 1 && var_tmp[0] == VARIABLE_EXPAND_EMPTY) - { + if (var_len == 1 && var_tmp[0] == VARIABLE_EXPAND_EMPTY) { var_val = env_var_t::missing_var(); - } - else - { + } else { 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) - { + if (is_ok) { tokenize_variable_array(var_val, var_item_list); const size_t slice_start = stop_pos; - if (slice_start < insize && instr.at(slice_start) == L'[') - { + if (slice_start < insize && instr.at(slice_start) == L'[') { wchar_t *slice_end; size_t bad_pos; - all_vars=0; + all_vars = 0; const wchar_t *in = instr.c_str(); - bad_pos = parse_slice(in + slice_start, &slice_end, var_idx_list, var_pos_list, var_item_list.size()); - if (bad_pos != 0) - { - append_syntax_error(errors, - stop_pos + bad_pos, - L"Invalid index value"); + bad_pos = parse_slice(in + slice_start, &slice_end, var_idx_list, + var_pos_list, var_item_list.size()); + if (bad_pos != 0) { + append_syntax_error(errors, stop_pos + bad_pos, L"Invalid index value"); is_ok = false; break; } - stop_pos = (slice_end-in); + 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()) - { + // Check that we are within array bounds. If not, truncate the list to + // exit. + if (tmp < 1 || (size_t)tmp > var_item_list.size()) { size_t var_src_pos = var_pos_list.at(j); - /* The slice was parsed starting at stop_pos, so we have to add that to the error position */ - append_syntax_error(errors, - slice_start + var_src_pos, + // The slice was parsed starting at stop_pos, so we have to add that + // to the error position. + append_syntax_error(errors, slice_start + var_src_pos, ARRAY_BOUNDS_ERR); is_ok = false; 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); + } 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 + // string_values is the new var_item_list. var_item_list.swap(string_values); } } - if (is_ok) - { - if (is_single) - { + if (is_ok) { + if (is_single) { wcstring res(instr, 0, i); - if (i > 0) - { - if (instr.at(i-1) != VARIABLE_EXPAND_SINGLE) - { + if (i > 0) { + if (instr.at(i - 1) != VARIABLE_EXPAND_SINGLE) { res.push_back(INTERNAL_SEPARATOR); - } - else if (var_item_list.empty() || var_item_list.front().empty()) - { - // first expansion is empty, but we need to recursively expand + } else if (var_item_list.empty() || var_item_list.front().empty()) { + // First expansion is empty, but we need to recursively expand. res.push_back(VARIABLE_EXPAND_EMPTY); } } - for (size_t j=0; j 0) - { - if (instr.at(i-1) != VARIABLE_EXPAND) - { + if (i > 0) { + if (instr.at(i - 1) != VARIABLE_EXPAND) { new_in.push_back(INTERNAL_SEPARATOR); - } - else if (next.empty()) - { + } else if (next.empty()) { new_in.push_back(VARIABLE_EXPAND_EMPTY); } } @@ -1179,64 +902,51 @@ static int expand_variables(const wcstring &instr, std::vector *ou is_ok &= expand_variables(new_in, out, i, errors); } } - } } } return is_ok; - } - else - { - // even with no value, we still need to parse out slice syntax - // Behave as though we had 1 value, so $foo[1] always works. + } else { + // Even with no value, we still need to parse out slice syntax. Behave as though we + // had 1 value, so $foo[1] always works. const size_t slice_start = stop_pos; - if (slice_start < insize && instr.at(slice_start) == L'[') - { + if (slice_start < insize && instr.at(slice_start) == L'[') { const wchar_t *in = instr.c_str(); wchar_t *slice_end; size_t bad_pos; - bad_pos = parse_slice(in + slice_start, &slice_end, var_idx_list, var_pos_list, 1); - if (bad_pos != 0) - { - append_syntax_error(errors, - stop_pos + bad_pos, - L"Invalid index value"); + bad_pos = + parse_slice(in + slice_start, &slice_end, var_idx_list, var_pos_list, 1); + if (bad_pos != 0) { + append_syntax_error(errors, stop_pos + bad_pos, L"Invalid index value"); is_ok = 0; return is_ok; } - stop_pos = (slice_end-in); + stop_pos = (slice_end - in); - // validate that the parsed indexes are valid - for (size_t j=0; j 0 && instr.at(i-1) == VARIABLE_EXPAND_SINGLE) - { + if (i > 0 && instr.at(i - 1) == VARIABLE_EXPAND_SINGLE) { res.push_back(VARIABLE_EXPAND_EMPTY); } assert(stop_pos <= insize); @@ -1249,21 +959,18 @@ static int expand_variables(const wcstring &instr, std::vector *ou } } - if (!empty) - { + if (!empty) { append_completion(out, instr); } return is_ok; } -/** - Perform bracket expansion -*/ -static expand_error_t expand_brackets(const wcstring &instr, expand_flags_t flags, std::vector *out, parse_error_list_t *errors) -{ +/// Perform bracket expansion. +static expand_error_t expand_brackets(const wcstring &instr, expand_flags_t flags, + std::vector *out, parse_error_list_t *errors) { bool syntax_error = false; - int bracket_count=0; + int bracket_count = 0; const wchar_t *bracket_begin = NULL, *bracket_end = NULL; const wchar_t *last_sep = NULL; @@ -1271,96 +978,71 @@ static expand_error_t expand_brackets(const wcstring &instr, expand_flags_t flag const wchar_t *item_begin; size_t length_preceding_brackets, length_following_brackets, tot_len; - const wchar_t * const in = instr.c_str(); + const wchar_t *const in = instr.c_str(); - /* Locate the first non-nested bracket pair */ - for (const wchar_t *pos = in; (*pos) && !syntax_error; pos++) - { - switch (*pos) - { - case BRACKET_BEGIN: - { - if (bracket_count == 0) - bracket_begin = pos; + // Locate the first non-nested bracket pair. + for (const wchar_t *pos = in; (*pos) && !syntax_error; pos++) { + switch (*pos) { + case BRACKET_BEGIN: { + if (bracket_count == 0) bracket_begin = pos; bracket_count++; break; - } - case BRACKET_END: - { + case BRACKET_END: { bracket_count--; - if (bracket_count < 0) - { + if (bracket_count < 0) { syntax_error = true; - } - else if (bracket_count == 0) - { + } else if (bracket_count == 0) { bracket_end = pos; break; } - } - case BRACKET_SEP: - { - if (bracket_count == 1) - last_sep = pos; + case BRACKET_SEP: { + if (bracket_count == 1) last_sep = pos; } } } - if (bracket_count > 0) - { - if (!(flags & EXPAND_FOR_COMPLETIONS)) - { + if (bracket_count > 0) { + if (!(flags & EXPAND_FOR_COMPLETIONS)) { syntax_error = true; - } - else - { - /* The user hasn't typed an end bracket yet; make one up and append it, then expand that. */ + } else { + // The user hasn't typed an end bracket yet; make one up and append it, then expand + // that. wcstring mod; - if (last_sep) - { - mod.append(in, bracket_begin-in+1); - mod.append(last_sep+1); + if (last_sep) { + mod.append(in, bracket_begin - in + 1); + mod.append(last_sep + 1); mod.push_back(BRACKET_END); - } - else - { + } else { mod.append(in); mod.push_back(BRACKET_END); } - /* Note: this code looks very fishy, apparently it has never worked */ + // Note: this code looks very fishy, apparently it has never worked. return expand_brackets(mod, 1, out, errors); } } - if (syntax_error) - { - append_syntax_error(errors, - SOURCE_LOCATION_UNKNOWN, - _(L"Mismatched brackets")); + if (syntax_error) { + append_syntax_error(errors, SOURCE_LOCATION_UNKNOWN, _(L"Mismatched brackets")); return EXPAND_ERROR; } - if (bracket_begin == NULL) - { + if (bracket_begin == NULL) { append_completion(out, instr); return EXPAND_OK; } - length_preceding_brackets = (bracket_begin-in); - length_following_brackets = wcslen(bracket_end)-1; - tot_len = length_preceding_brackets+length_following_brackets; - item_begin = bracket_begin+1; - for (const wchar_t *pos =(bracket_begin+1); true; pos++) - { - if (bracket_count == 0) - { - if ((*pos == BRACKET_SEP) || (pos==bracket_end)) - { + length_preceding_brackets = (bracket_begin - in); + length_following_brackets = wcslen(bracket_end) - 1; + tot_len = length_preceding_brackets + length_following_brackets; + item_begin = bracket_begin + 1; + for (const wchar_t *pos = (bracket_begin + 1); true; 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); @@ -1369,142 +1051,122 @@ static expand_error_t expand_brackets(const wcstring &instr, expand_flags_t flag whole_item.append(bracket_end + 1); expand_brackets(whole_item, flags, out, errors); - item_begin = pos+1; - if (pos == bracket_end) - break; + item_begin = pos + 1; + if (pos == bracket_end) break; } } - if (*pos == BRACKET_BEGIN) - { + if (*pos == BRACKET_BEGIN) { bracket_count++; } - if (*pos == BRACKET_END) - { + if (*pos == BRACKET_END) { bracket_count--; } } return EXPAND_OK; } -/** - Perform cmdsubst expansion - */ -static int expand_cmdsubst(const wcstring &input, std::vector *out_list, parse_error_list_t *errors) -{ - wchar_t *paran_begin=0, *paran_end=0; +/// Perform cmdsubst expansion. +static int expand_cmdsubst(const wcstring &input, std::vector *out_list, + parse_error_list_t *errors) { + wchar_t *paran_begin = 0, *paran_end = 0; std::vector sub_res; size_t i, j; wchar_t *tail_begin = 0; - const wchar_t * const in = input.c_str(); + const wchar_t *const in = input.c_str(); int parse_ret; - switch (parse_ret = parse_util_locate_cmdsubst(in, ¶n_begin, ¶n_end, false)) - { - case -1: - append_syntax_error(errors, - SOURCE_LOCATION_UNKNOWN, - L"Mismatched parenthesis"); + switch (parse_ret = parse_util_locate_cmdsubst(in, ¶n_begin, ¶n_end, false)) { + case -1: { + append_syntax_error(errors, SOURCE_LOCATION_UNKNOWN, L"Mismatched parenthesis"); return 0; - case 0: + } + case 0: { append_completion(out_list, input); return 1; - case 1: - + } + case 1: { break; + } } - const wcstring subcmd(paran_begin + 1, paran_end-paran_begin - 1); + const wcstring subcmd(paran_begin + 1, paran_end - paran_begin - 1); - if (exec_subshell(subcmd, sub_res, true /* do apply exit status */) == -1) - { - append_cmdsub_error(errors, SOURCE_LOCATION_UNKNOWN, L"Unknown error while evaulating command substitution"); + if (exec_subshell(subcmd, sub_res, true /* do apply exit status */) == -1) { + append_cmdsub_error(errors, SOURCE_LOCATION_UNKNOWN, + L"Unknown error while evaulating command substitution"); return 0; } tail_begin = paran_end + 1; - if (*tail_begin == L'[') - { + if (*tail_begin == L'[') { std::vector slice_idx; std::vector slice_source_positions; - const wchar_t * const slice_begin = tail_begin; + const wchar_t *const slice_begin = tail_begin; wchar_t *slice_end; size_t bad_pos; - bad_pos = parse_slice(slice_begin, &slice_end, slice_idx, slice_source_positions, sub_res.size()); - if (bad_pos != 0) - { + bad_pos = + parse_slice(slice_begin, &slice_end, slice_idx, slice_source_positions, sub_res.size()); + if (bad_pos != 0) { append_syntax_error(errors, slice_begin - in + bad_pos, L"Invalid index value"); return 0; - } - else - { + } else { wcstring_list_t sub_res2; tail_begin = slice_end; - for (i=0; i < slice_idx.size(); i++) - { + for (i = 0; i < slice_idx.size(); i++) { long idx = slice_idx.at(i); - if (idx < 1 || (size_t)idx > sub_res.size()) - { + if (idx < 1 || (size_t)idx > sub_res.size()) { size_t pos = slice_source_positions.at(i); - append_syntax_error(errors, - slice_begin - in + pos, - ARRAY_BOUNDS_ERR); + append_syntax_error(errors, slice_begin - in + pos, ARRAY_BOUNDS_ERR); return 0; } - idx = idx-1; + 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; // ?? + // debug( 0, L"Pushing item '%ls' with index %d onto sliced result", al_get( + // sub_res, idx ), idx ); + // sub_res[idx] = 0; // ?? } sub_res = sub_res2; } } - - /* - Recursively call ourselves to expand any remaining command - substitutions. The result of this recursive call using the tail - of the string is inserted into the tail_expand array list - */ + // 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(tail_begin, &tail_expand, errors /* TODO: offset error locations */); + expand_cmdsubst(tail_begin, &tail_expand, errors); // TODO: offset error locations - /* - Combine the result of the current command substitution with the - result of the recursive tail expansion - */ - for (i=0; i *out return 1; } -/* Given that input[0] is HOME_DIRECTORY or tilde (ugh), return the user's name. Return the empty string if it is just a tilde. Also return by reference the index of the first character of the remaining part of the string (e.g. the subsequent slash) */ -static wcstring get_home_directory_name(const wcstring &input, size_t *out_tail_idx) -{ - const wchar_t * const in = input.c_str(); +// Given that input[0] is HOME_DIRECTORY or tilde (ugh), return the user's name. Return the empty +// string if it is just a tilde. Also return by reference the index of the first character of the +// remaining part of the string (e.g. the subsequent slash). +static wcstring get_home_directory_name(const wcstring &input, size_t *out_tail_idx) { + const wchar_t *const in = input.c_str(); assert(in[0] == HOME_DIRECTORY || in[0] == L'~'); size_t tail_idx; const wchar_t *name_end = wcschr(in, L'/'); - if (name_end) - { + if (name_end) { tail_idx = name_end - in; - } - else - { + } else { tail_idx = wcslen(in); } *out_tail_idx = tail_idx; return input.substr(1, tail_idx - 1); } -/** Attempts tilde expansion of the string specified, modifying it in place. */ -static void expand_home_directory(wcstring &input) -{ - if (! input.empty() && input.at(0) == HOME_DIRECTORY) - { +/// Attempts tilde expansion of the string specified, modifying it in place. +static void expand_home_directory(wcstring &input) { + if (!input.empty() && input.at(0) == HOME_DIRECTORY) { size_t tail_idx; wcstring username = get_home_directory_name(input, &tail_idx); - + bool tilde_error = false; wcstring home; - if (username.empty()) - { - /* Current users home directory */ + if (username.empty()) { + // Current users home directory. home = env_get_string(L"HOME"); tail_idx = 1; - } - else - { - /* Some other users home directory */ + } else { + // Some other users home directory. std::string name_cstr = wcs2string(username); struct passwd *userinfo = getpwnam(name_cstr.c_str()); - if (userinfo == NULL) - { + if (userinfo == NULL) { tilde_error = true; - } - else - { + } else { home = str2wcstring(userinfo->pw_dir); } } - + wchar_t *realhome = wrealpath(home, NULL); - - if (! tilde_error && realhome) - { + + if (!tilde_error && realhome) { input.replace(input.begin(), input.begin() + tail_idx, realhome); - } - else - { + } else { input[0] = L'~'; } free((void *)realhome); } } -void expand_tilde(wcstring &input) -{ - // Avoid needless COW behavior by ensuring we use const at +void expand_tilde(wcstring &input) { + // Avoid needless COW behavior by ensuring we use const at. const wcstring &tmp = input; - if (! tmp.empty() && tmp.at(0) == L'~') - { + if (!tmp.empty() && tmp.at(0) == L'~') { input.at(0) = HOME_DIRECTORY; expand_home_directory(input); } } -static void unexpand_tildes(const wcstring &input, std::vector *completions) -{ - // If input begins with tilde, then try to replace the corresponding string in each completion with the tilde - // If it does not, there's nothing to do - if (input.empty() || input.at(0) != L'~') - return; +static void unexpand_tildes(const wcstring &input, std::vector *completions) { + // If input begins with tilde, then try to replace the corresponding string in each completion + // with the tilde. If it does not, there's nothing to do. + if (input.empty() || input.at(0) != L'~') return; - // We only operate on completions that replace their contents - // If we don't have any, we're done. + // We only operate on completions that replace their contents. If we don't have any, we're done. // In particular, empty vectors are common. bool has_candidate_completion = false; - for (size_t i=0; i < completions->size(); i++) - { - if (completions->at(i).flags & COMPLETE_REPLACES_TOKEN) - { + for (size_t i = 0; i < completions->size(); i++) { + if (completions->at(i).flags & COMPLETE_REPLACES_TOKEN) { has_candidate_completion = true; break; } } - if (! has_candidate_completion) - return; + if (!has_candidate_completion) return; size_t tail_idx; wcstring username_with_tilde = L"~"; username_with_tilde.append(get_home_directory_name(input, &tail_idx)); - // Expand username_with_tilde + // Expand username_with_tilde. wcstring home = username_with_tilde; expand_tilde(home); - // Now for each completion that starts with home, replace it with the username_with_tilde - for (size_t i=0; i < completions->size(); i++) - { + // Now for each completion that starts with home, replace it with the username_with_tilde. + for (size_t i = 0; i < completions->size(); i++) { completion_t &comp = completions->at(i); - if ((comp.flags & COMPLETE_REPLACES_TOKEN) && string_prefixes_string(home, comp.completion)) - { + if ((comp.flags & COMPLETE_REPLACES_TOKEN) && + string_prefixes_string(home, comp.completion)) { comp.completion.replace(0, home.size(), username_with_tilde); - // And mark that our tilde is literal, so it doesn't try to escape it + // And mark that our tilde is literal, so it doesn't try to escape it. comp.flags |= COMPLETE_DONT_ESCAPE_TILDES; } } } -// If the given path contains the user's home directory, replace that with a tilde -// We don't try to be smart about case insensitivity, etc. -wcstring replace_home_directory_with_tilde(const wcstring &str) -{ - // only absolute paths get this treatment +// If the given path contains the user's home directory, replace that with a tilde. We don't try to +// be smart about case insensitivity, etc. +wcstring replace_home_directory_with_tilde(const wcstring &str) { + // Only absolute paths get this treatment. wcstring result = str; - if (string_prefixes_string(L"/", result)) - { + if (string_prefixes_string(L"/", result)) { wcstring home_directory = L"~"; expand_tilde(home_directory); - if (! string_suffixes_string(L"/", home_directory)) - { + if (!string_suffixes_string(L"/", home_directory)) { home_directory.push_back(L'/'); } - // Now check if the home_directory prefixes the string - if (string_prefixes_string(home_directory, result)) - { + // Now check if the home_directory prefixes the string. + if (string_prefixes_string(home_directory, result)) { // Success result.replace(0, home_directory.size(), L"~/"); } @@ -1657,278 +1293,235 @@ wcstring replace_home_directory_with_tilde(const wcstring &str) return result; } -/** - 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) -{ - /* Remove all instances of INTERNAL_SEPARATOR */ +/// 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) { + // Remove all instances of INTERNAL_SEPARATOR. str->erase(std::remove(str->begin(), str->end(), (wchar_t)INTERNAL_SEPARATOR), str->end()); - /* If conv is true, replace all instances of ANY_CHAR with '?', ANY_STRING with '*', ANY_STRING_RECURSIVE with '*' */ - if (conv) - { - for (size_t idx = 0; idx < str->size(); idx++) - { - switch (str->at(idx)) - { - case ANY_CHAR: + // If conv is true, replace all instances of ANY_CHAR with '?', ANY_STRING with '*', + // ANY_STRING_RECURSIVE with '*'. + if (conv) { + for (size_t idx = 0; idx < str->size(); idx++) { + switch (str->at(idx)) { + case ANY_CHAR: { str->at(idx) = L'?'; break; + } case ANY_STRING: - case ANY_STRING_RECURSIVE: + case ANY_STRING_RECURSIVE: { str->at(idx) = L'*'; break; + } } } } } -/** - * A stage in string expansion is represented as a function that takes an input and returns a list - * of output (by reference). We get flags and errors. It may return an error; if so expansion halts. - */ -typedef expand_error_t (*expand_stage_t)(const wcstring &input, std::vector *out, expand_flags_t flags, parse_error_list_t *errors); +/// A stage in string expansion is represented as a function that takes an input and returns a list +/// of output (by reference). We get flags and errors. It may return an error; if so expansion +/// halts. +typedef expand_error_t (*expand_stage_t)(const wcstring &input, std::vector *out, + expand_flags_t flags, parse_error_list_t *errors); -static expand_error_t expand_stage_cmdsubst(const wcstring &input, std::vector *out, expand_flags_t flags, parse_error_list_t *errors) -{ +static expand_error_t expand_stage_cmdsubst(const wcstring &input, std::vector *out, + expand_flags_t flags, parse_error_list_t *errors) { expand_error_t result = EXPAND_OK; - if (EXPAND_SKIP_CMDSUBST & flags) - { + if (EXPAND_SKIP_CMDSUBST & flags) { wchar_t *begin, *end; - if (parse_util_locate_cmdsubst(input.c_str(), &begin, &end, true) == 0) - { + if (parse_util_locate_cmdsubst(input.c_str(), &begin, &end, true) == 0) { append_completion(out, input); - } - else - { - append_cmdsub_error(errors, SOURCE_LOCATION_UNKNOWN, L"Command substitutions not allowed"); + } else { + append_cmdsub_error(errors, SOURCE_LOCATION_UNKNOWN, + L"Command substitutions not allowed"); result = EXPAND_ERROR; } - } - else - { + } else { int cmdsubst_ok = expand_cmdsubst(input, out, errors); - if (! cmdsubst_ok) - { + if (!cmdsubst_ok) { result = EXPAND_ERROR; } } return result; } -static expand_error_t expand_stage_variables(const wcstring &input, std::vector *out, expand_flags_t flags, parse_error_list_t *errors) -{ - /* - We accept incomplete strings here, since complete uses - expand_string to expand incomplete strings from the - commandline. - */ +static expand_error_t expand_stage_variables(const wcstring &input, std::vector *out, + expand_flags_t flags, parse_error_list_t *errors) { + // We accept incomplete strings here, since complete uses expand_string to expand incomplete + // strings from the commandline. wcstring next; unescape_string(input, &next, UNESCAPE_SPECIAL | UNESCAPE_INCOMPLETE); - - if (EXPAND_SKIP_VARIABLES & flags) - { - for (size_t i=0; i < next.size(); i++) - { - if (next.at(i) == VARIABLE_EXPAND) - { + + if (EXPAND_SKIP_VARIABLES & flags) { + for (size_t i = 0; i < next.size(); i++) { + if (next.at(i) == VARIABLE_EXPAND) { next[i] = L'$'; } } append_completion(out, next); - } - else - { - if (!expand_variables(next, out, next.size(), errors)) - { + } else { + if (!expand_variables(next, out, next.size(), errors)) { return EXPAND_ERROR; } } return EXPAND_OK; } -static expand_error_t expand_stage_brackets(const wcstring &input, std::vector *out, expand_flags_t flags, parse_error_list_t *errors) -{ +static expand_error_t expand_stage_brackets(const wcstring &input, std::vector *out, + expand_flags_t flags, parse_error_list_t *errors) { return expand_brackets(input, flags, out, errors); } -static expand_error_t expand_stage_home_and_pid(const wcstring &input, std::vector *out, expand_flags_t flags, parse_error_list_t *errors) -{ +static expand_error_t expand_stage_home_and_pid(const wcstring &input, + std::vector *out, + expand_flags_t flags, parse_error_list_t *errors) { wcstring next = input; - - if (!(EXPAND_SKIP_HOME_DIRECTORIES & flags)) - { + + if (!(EXPAND_SKIP_HOME_DIRECTORIES & flags)) { expand_home_directory(next); } - - if (flags & EXPAND_FOR_COMPLETIONS) - { - if (! next.empty() && next.at(0) == PROCESS_EXPAND) - { + + if (flags & EXPAND_FOR_COMPLETIONS) { + if (!next.empty() && next.at(0) == PROCESS_EXPAND) { expand_pid(next, flags, out, NULL); return EXPAND_OK; - } - else - { + } else { append_completion(out, next); } - } - else if (! expand_pid(next, flags, out, errors)) - { + } else if (!expand_pid(next, flags, out, errors)) { return EXPAND_ERROR; } return EXPAND_OK; } -static expand_error_t expand_stage_wildcards(const wcstring &input, std::vector *out, expand_flags_t flags, parse_error_list_t *errors) -{ +static expand_error_t expand_stage_wildcards(const wcstring &input, std::vector *out, + expand_flags_t flags, parse_error_list_t *errors) { expand_error_t result = EXPAND_OK; wcstring path_to_expand = input; - + remove_internal_separator(&path_to_expand, (EXPAND_SKIP_WILDCARDS & flags) ? true : false); const bool has_wildcard = wildcard_has(path_to_expand, true /* internal, i.e. ANY_CHAR */); - - if (has_wildcard && (flags & EXECUTABLES_ONLY)) - { - /* Don't do wildcard expansion for executables. See #785. Make them expand to nothing here. */ - } - else if (((flags & EXPAND_FOR_COMPLETIONS) && (!(flags & EXPAND_SKIP_WILDCARDS))) || - has_wildcard) - { - /* We either have a wildcard, or we don't have a wildcard but we're doing completion expansion (so we want to get the completion of a file path). Note that if EXPAND_SKIP_WILDCARDS is set, we stomped wildcards in remove_internal_separator above, so there actually aren't any. - - So we're going to treat this input as a file path. Compute the "working directories", which may be CDPATH if the special flag is set. - */ - + + if (has_wildcard && (flags & EXECUTABLES_ONLY)) { + // Don't do wildcard expansion for executables. See #785. Make them expand to nothing here. + } else if (((flags & EXPAND_FOR_COMPLETIONS) && (!(flags & EXPAND_SKIP_WILDCARDS))) || + has_wildcard) { + // We either have a wildcard, or we don't have a wildcard but we're doing completion + // expansion (so we want to get the completion of a file path). Note that if + // EXPAND_SKIP_WILDCARDS is set, we stomped wildcards in remove_internal_separator above, so + // there actually aren't any. + // + // So we're going to treat this input as a file path. Compute the "working directories", + // which may be CDPATH if the special flag is set. const wcstring working_dir = env_get_pwd_slash(); wcstring_list_t effective_working_dirs; bool for_cd = !!(flags & EXPAND_SPECIAL_FOR_CD); bool for_command = !!(flags & EXPAND_SPECIAL_FOR_COMMAND); - if (!for_cd && !for_command) - { - /* Common case */ + if (!for_cd && !for_command) { + // Common case. effective_working_dirs.push_back(working_dir); - } - else - { - /* Either EXPAND_SPECIAL_FOR_COMMAND or EXPAND_SPECIAL_FOR_CD. We can handle these mostly the same. - There's the following differences: - 1. An empty CDPATH should be treated as '.', but an empty PATH should be left empty (no commands can be found). - 2. PATH is only "one level," while CDPATH is multiple levels. That is, input like 'foo/bar' should resolve - against CDPATH, but not PATH. - - In either case, we ignore the path if we start with ./ or /. - Also ignore it if we are doing command completion and we contain a slash, per IEEE 1003.1, chapter 8 under PATH - */ + } else { + // Either EXPAND_SPECIAL_FOR_COMMAND or EXPAND_SPECIAL_FOR_CD. We can handle these + // mostly the same. There's the following differences: + // + // 1. An empty CDPATH should be treated as '.', but an empty PATH should be left empty + // (no commands can be found). + // + // 2. PATH is only "one level," while CDPATH is multiple levels. That is, input like + // 'foo/bar' should resolve against CDPATH, but not PATH. + // + // In either case, we ignore the path if we start with ./ or /. Also ignore it if we are + // doing command completion and we contain a slash, per IEEE 1003.1, chapter 8 under + // PATH. if (string_prefixes_string(L"/", path_to_expand) || string_prefixes_string(L"./", path_to_expand) || string_prefixes_string(L"../", path_to_expand) || - (for_command && path_to_expand.find(L'/') != wcstring::npos)) - { + (for_command && path_to_expand.find(L'/') != wcstring::npos)) { effective_working_dirs.push_back(working_dir); - } - else - { - /* Get the PATH/CDPATH and cwd. Perhaps these should be passed in. - An empty CDPATH implies just the current directory, while an empty PATH is left empty. - */ + } else { + // Get the PATH/CDPATH and cwd. Perhaps these should be passed in. An empty CDPATH + // implies just the current directory, while an empty PATH is left empty. env_var_t paths = env_get_string(for_cd ? L"CDPATH" : L"PATH"); - if (paths.missing_or_empty()) - paths = for_cd ? L"." : L""; - - /* Tokenize it into directories */ + if (paths.missing_or_empty()) paths = for_cd ? L"." : L""; + + // Tokenize it into directories. wcstokenizer tokenizer(paths, ARRAY_SEP_STR); wcstring next_path; - while (tokenizer.next(next_path)) - { - /* Ensure that we use the working directory for relative cdpaths like "." */ - effective_working_dirs.push_back(path_apply_working_directory(next_path, working_dir)); - + while (tokenizer.next(next_path)) { + // Ensure that we use the working directory for relative cdpaths like ".". + effective_working_dirs.push_back( + path_apply_working_directory(next_path, working_dir)); } } } - + result = EXPAND_WILDCARD_NO_MATCH; std::vector expanded; - for (size_t wd_idx = 0; wd_idx < effective_working_dirs.size(); wd_idx++) - { - int local_wc_res = wildcard_expand_string(path_to_expand, effective_working_dirs.at(wd_idx), flags, &expanded); - if (local_wc_res > 0) - { - // Something matched,so overall we matched + for (size_t wd_idx = 0; wd_idx < effective_working_dirs.size(); wd_idx++) { + int local_wc_res = wildcard_expand_string( + path_to_expand, effective_working_dirs.at(wd_idx), flags, &expanded); + if (local_wc_res > 0) { + // Something matched,so overall we matched. result = EXPAND_WILDCARD_MATCH; - } - else if (local_wc_res < 0) - { + } else if (local_wc_res < 0) { // Cancellation result = EXPAND_ERROR; break; } } - + std::sort(expanded.begin(), expanded.end(), completion_t::is_naturally_less_than); out->insert(out->end(), expanded.begin(), expanded.end()); - } - else - { - /* Can't fully justify this check. I think it's that SKIP_WILDCARDS is used when completing to mean don't do file expansions, so if we're not doing file expansions, just drop this completion on the floor. */ - if (!(flags & EXPAND_FOR_COMPLETIONS)) - { + } else { + // Can't fully justify this check. I think it's that SKIP_WILDCARDS is used when completing + // to mean don't do file expansions, so if we're not doing file expansions, just drop this + // completion on the floor. + if (!(flags & EXPAND_FOR_COMPLETIONS)) { append_completion(out, path_to_expand); } } return result; } -expand_error_t expand_string(const wcstring &input, std::vector *out_completions, expand_flags_t flags, parse_error_list_t *errors) -{ - /* Early out. If we're not completing, and there's no magic in the input, we're done. */ - if (!(flags & EXPAND_FOR_COMPLETIONS) && expand_is_clean(input)) - { +expand_error_t expand_string(const wcstring &input, std::vector *out_completions, + expand_flags_t flags, parse_error_list_t *errors) { + // Early out. If we're not completing, and there's no magic in the input, we're done. + if (!(flags & EXPAND_FOR_COMPLETIONS) && expand_is_clean(input)) { append_completion(out_completions, input); return EXPAND_OK; } - - /* Our expansion stages */ - const expand_stage_t stages[] = - { - expand_stage_cmdsubst, - expand_stage_variables, - expand_stage_brackets, - expand_stage_home_and_pid, - expand_stage_wildcards - }; - - /* Load up our single initial completion */ + + // Our expansion stages. + const expand_stage_t stages[] = {expand_stage_cmdsubst, expand_stage_variables, + expand_stage_brackets, expand_stage_home_and_pid, + expand_stage_wildcards}; + + // Load up our single initial completion. std::vector completions, output_storage; append_completion(&completions, input); - + expand_error_t total_result = EXPAND_OK; - for (size_t stage_idx=0; total_result != EXPAND_ERROR && stage_idx < sizeof stages / sizeof *stages; stage_idx++) - { - for (size_t i=0; total_result != EXPAND_ERROR && i < completions.size(); i++) - { + for (size_t stage_idx = 0; + total_result != EXPAND_ERROR && stage_idx < sizeof stages / sizeof *stages; stage_idx++) { + for (size_t i = 0; total_result != EXPAND_ERROR && i < completions.size(); i++) { const wcstring &next = completions.at(i).completion; expand_error_t this_result = stages[stage_idx](next, &output_storage, flags, errors); - /* If this_result was no match, but total_result is that we have a match, then don't change it */ - if (! (this_result == EXPAND_WILDCARD_NO_MATCH && total_result == EXPAND_WILDCARD_MATCH)) - { + // If this_result was no match, but total_result is that we have a match, then don't + // change it. + if (!(this_result == EXPAND_WILDCARD_NO_MATCH && + total_result == EXPAND_WILDCARD_MATCH)) { total_result = this_result; } } - - /* Output becomes our next stage's input */ + + // Output becomes our next stage's input. completions.swap(output_storage); output_storage.clear(); } - - if (total_result != EXPAND_ERROR) - { - /* Hack to un-expand tildes (see #647) */ - if (!(flags & EXPAND_SKIP_HOME_DIRECTORIES)) - { + + if (total_result != EXPAND_ERROR) { + // Hack to un-expand tildes (see #647). + if (!(flags & EXPAND_SKIP_HOME_DIRECTORIES)) { unexpand_tildes(input, &completions); } out_completions->insert(out_completions->end(), completions.begin(), completions.end()); @@ -1936,20 +1529,16 @@ expand_error_t expand_string(const wcstring &input, std::vector *o return total_result; } -bool expand_one(wcstring &string, expand_flags_t flags, parse_error_list_t *errors) -{ +bool expand_one(wcstring &string, expand_flags_t flags, parse_error_list_t *errors) { std::vector completions; bool result = false; - if ((!(flags & EXPAND_FOR_COMPLETIONS)) && expand_is_clean(string)) - { + if ((!(flags & EXPAND_FOR_COMPLETIONS)) && expand_is_clean(string)) { return true; } - if (expand_string(string, &completions, flags | EXPAND_NO_DESCRIPTIONS, errors)) - { - if (completions.size() == 1) - { + if (expand_string(string, &completions, flags | EXPAND_NO_DESCRIPTIONS, errors)) { + if (completions.size() == 1) { string = completions.at(0).completion; result = true; } @@ -1957,51 +1546,40 @@ bool expand_one(wcstring &string, expand_flags_t flags, parse_error_list_t *erro return result; } +// https://github.com/fish-shell/fish-shell/issues/367 +// +// With them the Seed of Wisdom did I sow, +// And with my own hand labour'd it to grow: +// And this was all the Harvest that I reap'd--- +// "I came like Water, and like Wind I go." -/* - -https://github.com/fish-shell/fish-shell/issues/367 - -With them the Seed of Wisdom did I sow, -And with my own hand labour'd it to grow: -And this was all the Harvest that I reap'd--- -"I came like Water, and like Wind I go." - -*/ - -static std::string escape_single_quoted_hack_hack_hack_hack(const char *str) -{ +static std::string escape_single_quoted_hack_hack_hack_hack(const char *str) { std::string result; size_t len = strlen(str); result.reserve(len + 2); result.push_back('\''); - for (size_t i=0; i < len; i++) - { + for (size_t i = 0; i < len; i++) { char c = str[i]; - // Escape backslashes and single quotes only - if (c == '\\' || c == '\'') - result.push_back('\\'); + // Escape backslashes and single quotes only. + if (c == '\\' || c == '\'') result.push_back('\\'); result.push_back(c); } result.push_back('\''); return result; } -bool fish_xdm_login_hack_hack_hack_hack(std::vector *cmds, int argc, const char * const *argv) -{ +bool fish_xdm_login_hack_hack_hack_hack(std::vector *cmds, int argc, + const char *const *argv) { bool result = false; - if (cmds && cmds->size() == 1) - { + if (cmds && cmds->size() == 1) { const std::string &cmd = cmds->at(0); - if (cmd == "exec \"${@}\"" || cmd == "exec \"$@\"") - { - /* We're going to construct a new command that starts with exec, and then has the remaining arguments escaped */ + if (cmd == "exec \"${@}\"" || cmd == "exec \"$@\"") { + // We're going to construct a new command that starts with exec, and then has the + // remaining arguments escaped. std::string new_cmd = "exec"; - for (int i=1; i < argc; i++) - { + for (int i = 1; i < argc; i++) { const char *arg = argv[i]; - if (arg) - { + if (arg) { new_cmd.push_back(' '); new_cmd.append(escape_single_quoted_hack_hack_hack_hack(arg)); } @@ -2014,39 +1592,33 @@ bool fish_xdm_login_hack_hack_hack_hack(std::vector *cmds, int argc return result; } -bool expand_abbreviation(const wcstring &src, wcstring *output) -{ - if (src.empty()) - return false; +bool expand_abbreviation(const wcstring &src, wcstring *output) { + if (src.empty()) return false; - /* Get the abbreviations. Return false if we have none */ + // Get the abbreviations. Return false if we have none. env_var_t var = env_get_string(USER_ABBREVIATIONS_VARIABLE_NAME); - if (var.missing_or_empty()) - return false; + if (var.missing_or_empty()) return false; bool result = false; wcstring line; wcstokenizer tokenizer(var, ARRAY_SEP_STR); - while (tokenizer.next(line)) - { - /* Line is expected to be of the form 'foo=bar' or 'foo bar'. Parse out the first = or space. Silently skip on failure (no equals, or equals at the end or beginning). Try to avoid copying any strings until we are sure this is a match. */ + while (tokenizer.next(line)) { + // Line is expected to be of the form 'foo=bar' or 'foo bar'. Parse out the first = or + // space. Silently skip on failure (no equals, or equals at the end or beginning). Try to + // avoid copying any strings until we are sure this is a match. size_t equals_pos = line.find(L'='); size_t space_pos = line.find(L' '); size_t separator = mini(equals_pos, space_pos); - if (separator == wcstring::npos || separator == 0 || separator + 1 == line.size()) - continue; + if (separator == wcstring::npos || separator == 0 || separator + 1 == line.size()) continue; - /* Find the character just past the end of the command. Walk backwards, skipping spaces. */ + // Find the character just past the end of the command. Walk backwards, skipping spaces. size_t cmd_end = separator; - while (cmd_end > 0 && iswspace(line.at(cmd_end - 1))) - cmd_end--; + while (cmd_end > 0 && iswspace(line.at(cmd_end - 1))) cmd_end--; - /* See if this command matches */ - if (line.compare(0, cmd_end, src) == 0) - { - /* Success. Set output to everythign past the end of the string. */ - if (output != NULL) - output->assign(line, separator + 1, wcstring::npos); + // See if this command matches. + if (line.compare(0, cmd_end, src) == 0) { + // Success. Set output to everythign past the end of the string. + if (output != NULL) output->assign(line, separator + 1, wcstring::npos); result = true; break; diff --git a/src/expand.h b/src/expand.h index d0b5e38b0..a0efd1fd9 100644 --- a/src/expand.h +++ b/src/expand.h @@ -1,175 +1,159 @@ -/**\file expand.h - - Prototypes for string expansion functions. These functions perform - several kinds of parameter expansion. There are a lot of issues - with regards to memory allocation. Overall, these functions would - benefit from using a more clever memory allocation scheme, perhaps - an evil combination of talloc, string buffers and reference - counting. -*/ +// Prototypes for string expansion functions. These functions perform several kinds of parameter +// expansion. There are a lot of issues with regards to memory allocation. Overall, these functions +// would benefit from using a more clever memory allocation scheme, perhaps an evil combination of +// talloc, string buffers and reference counting. #ifndef FISH_EXPAND_H #define FISH_EXPAND_H #include "config.h" +#include +#include #include #include -#include -#include #include "common.h" #include "parse_constants.h" -enum -{ - // Flag specifying that cmdsubst expansion should be skipped. +enum { + /// Flag specifying that cmdsubst expansion should be skipped. EXPAND_SKIP_CMDSUBST = 1 << 0, - // Flag specifying that variable expansion should be skipped. + /// Flag specifying that variable expansion should be skipped. EXPAND_SKIP_VARIABLES = 1 << 1, - // Flag specifying that wildcard expansion should be skipped. + /// Flag specifying that wildcard expansion should be skipped. EXPAND_SKIP_WILDCARDS = 1 << 2, - // The expansion is being done for tab or auto completions. Returned - // completions may have the wildcard as a prefix instead of a match. + /// The expansion is being done for tab or auto completions. Returned completions may have the + /// wildcard as a prefix instead of a match. EXPAND_FOR_COMPLETIONS = 1 << 3, - // Only match files that are executable by the current user. Only - // applicable together with ACCEPT_INCOMPLETE. + /// Only match files that are executable by the current user. Only applicable together with + /// ACCEPT_INCOMPLETE. EXECUTABLES_ONLY = 1 << 4, - // Only match directories. Only applicable together with ACCEPT_INCOMPLETE. + /// Only match directories. Only applicable together with ACCEPT_INCOMPLETE. DIRECTORIES_ONLY = 1 << 5, - // Don't generate descriptions. + /// Don't generate descriptions. EXPAND_NO_DESCRIPTIONS = 1 << 6, - // Don't expand jobs (but you can still expand processes). This is because + /// Don't expand jobs (but you can still expand processes). This is because // job expansion is not thread safe. EXPAND_SKIP_JOBS = 1 << 7, - // Don't expand home directories. + /// Don't expand home directories. EXPAND_SKIP_HOME_DIRECTORIES = 1 << 8, - // Allow fuzzy matching. + /// Allow fuzzy matching. EXPAND_FUZZY_MATCH = 1 << 9, - // Disallow directory abbreviations like /u/l/b for /usr/local/bin. Only - // applicable if EXPAND_FUZZY_MATCH is set. + /// Disallow directory abbreviations like /u/l/b for /usr/local/bin. Only applicable if + /// EXPAND_FUZZY_MATCH is set. EXPAND_NO_FUZZY_DIRECTORIES = 1 << 10, - // Do expansions specifically to support cd - // This means using CDPATH as a list of potential working directories + /// Do expansions specifically to support cd. This means using CDPATH as a list of potential + /// working directories. EXPAND_SPECIAL_FOR_CD = 1 << 11, - // Do expansions specifically to support external command completions. - // This means using PATH as a list of potential working directories + /// Do expansions specifically to support external command completions. This means using PATH as + // a list of potential working directories. EXPAND_SPECIAL_FOR_COMMAND = 1 << 12 }; typedef int expand_flags_t; class completion_t; -enum -{ - // Character representing a home directory. +enum { + /// Character representing a home directory. HOME_DIRECTORY = EXPAND_RESERVED_BASE, - // Character representing process expansion. + /// Character representing process expansion. PROCESS_EXPAND, - // Character representing variable expansion. + /// Character representing variable expansion. VARIABLE_EXPAND, - // Character representing variable expansion into a single element. + /// Character representing variable expansion into a single element. VARIABLE_EXPAND_SINGLE, - // Character representing the start of a bracket expansion. + /// Character representing the start of a bracket expansion. BRACKET_BEGIN, - // Character representing the end of a bracket expansion. + /// Character representing the end of a bracket expansion. BRACKET_END, - // Character representing separation between two bracket elements. + /// Character representing separation between two bracket elements. BRACKET_SEP, - // Separate subtokens in a token with this character. + /// Separate subtokens in a token with this character. INTERNAL_SEPARATOR, - // Character representing an empty variable expansion. Only used - // transitively while expanding variables. + /// Character representing an empty variable expansion. Only used transitively while expanding + /// variables. VARIABLE_EXPAND_EMPTY, - // This is a special psuedo-char that is not used other than to mark the - // end of the the special characters so we can sanity check the enum range. + /// This is a special psuedo-char that is not used other than to mark the end of the the special + /// characters so we can sanity check the enum range. EXPAND_SENTINAL }; -/** These are the possible return values for expand_string. Note how zero value is the only error. */ -enum expand_error_t -{ - /** Error */ +/// These are the possible return values for expand_string. Note how zero value is the only error. +enum expand_error_t { + /// Error EXPAND_ERROR, - /** Ok */ + /// Ok EXPAND_OK, - /** Ok, a wildcard in the string matched no files */ + /// Ok, a wildcard in the string matched no files. EXPAND_WILDCARD_NO_MATCH, - /* Ok, a wildcard in the string matched a file */ + /// 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. */ +/// Character for separating two array elements. We use 30, i.e. the ascii record separator since +/// that seems logical. #define ARRAY_SEP ((wchar_t)(0x1e)) -/** String containing the character for separating two array elements */ +/// String containing the character for separating two array elements. #define ARRAY_SEP_STR L"\x1e" -/** - Error issued on array out of bounds -*/ +/// Error issued on array out of bounds. #define ARRAY_BOUNDS_ERR _(L"Array index out of bounds") -/** - Perform various forms of expansion on in, such as tilde expansion - (\~USER becomes the users home directory), variable expansion - (\$VAR_NAME becomes the value of the environment variable VAR_NAME), - cmdsubst expansion and wildcard expansion. The results are inserted - into the list out. +/// Perform various forms of expansion on in, such as tilde expansion (\~USER becomes the users home +/// directory), variable expansion (\$VAR_NAME becomes the value of the environment variable +/// VAR_NAME), cmdsubst expansion and wildcard expansion. The results are inserted into the list +/// out. +/// +/// If the parameter does not need expansion, it is copied into the list out. +/// +/// \param input The parameter to expand +/// \param output The list to which the result will be appended. +/// \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 +/// \param errors Resulting errors, or NULL to ignore +/// +/// \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 expand_error_t expand_string(const wcstring &input, std::vector *output, + expand_flags_t flags, parse_error_list_t *errors); - If the parameter does not need expansion, it is copied into the list - out. - - \param input The parameter to expand - \param output The list to which the result will be appended. - \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 - \param errors Resulting errors, or NULL to ignore - \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 expand_error_t expand_string(const wcstring &input, std::vector *output, expand_flags_t flags, parse_error_list_t *errors); - - -/** - expand_one is identical to expand_string, except it will fail if in - expands to more than one string. This is used for expanding command - names. - - \param inout_str The parameter to expand in-place - \param flag Specifies if any expansion pass should be skipped. Legal values are any combination of EXPAND_SKIP_CMDSUBST EXPAND_SKIP_VARIABLES and EXPAND_SKIP_WILDCARDS - \param errors Resulting errors, or NULL to ignore - \return Whether expansion succeded -*/ +/// expand_one is identical to expand_string, except it will fail if in expands to more than one +/// string. This is used for expanding command names. +/// +/// \param inout_str The parameter to expand in-place +/// \param flag Specifies if any expansion pass should be skipped. Legal values are any combination +/// of EXPAND_SKIP_CMDSUBST EXPAND_SKIP_VARIABLES and EXPAND_SKIP_WILDCARDS +/// \param errors Resulting errors, or NULL to ignore +/// +/// \return Whether expansion succeded bool expand_one(wcstring &inout_str, expand_flags_t flags, parse_error_list_t *errors = NULL); -/** - Convert the variable value to a human readable form, i.e. escape things, handle arrays, etc. Suitable for pretty-printing. The result must be free'd! - - \param in the value to escape -*/ +/// Convert the variable value to a human readable form, i.e. escape things, handle arrays, etc. +/// Suitable for pretty-printing. The result must be free'd! +/// +/// \param in the value to escape wcstring expand_escape_variable(const wcstring &in); -/** - Perform tilde expansion and nothing else on the specified string, which is modified in place. - - \param input the string to tilde expand -*/ +/// Perform tilde expansion and nothing else on the specified string, which is modified in place. +/// +/// \param input the string to tilde expand void expand_tilde(wcstring &input); -/** Perform the opposite of tilde expansion on the string, which is modified in place */ +/// Perform the opposite of tilde expansion on the string, which is modified in place. wcstring replace_home_directory_with_tilde(const wcstring &str); -/** - Testing function for getting all process names. -*/ +/// Testing function for getting all process names. std::vector expand_get_all_process_names(void); -/** Abbreviation support. Expand src as an abbreviation, returning true if one was found, false if not. If result is not-null, returns the abbreviation by reference. */ +/// Abbreviation support. Expand src as an abbreviation, returning true if one was found, false if +/// not. If result is not-null, returns the abbreviation by reference. #define USER_ABBREVIATIONS_VARIABLE_NAME L"fish_user_abbreviations" bool expand_abbreviation(const wcstring &src, wcstring *output); -/* Terrible hacks */ -bool fish_xdm_login_hack_hack_hack_hack(std::vector *cmds, int argc, const char * const *argv); - +// Terrible hacks +bool fish_xdm_login_hack_hack_hack_hack(std::vector *cmds, int argc, + const char *const *argv); #endif - - From 3e24ae80b3739ded7d4066349fc87d13f2b70727 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Thu, 28 Apr 2016 20:53:09 -0700 Subject: [PATCH 159/363] clarify fish_vi_mode deprecation warning Also, correct the Vi mode default escape timeout. I intended it to be 100 ms in my previous change but it ended up 10 ms which is far too short. A 10 ms delay will continue to cause problems for people running fish inside `screen`, `tmux`, or over high latency connections. --- share/functions/fish_vi_key_bindings.fish | 10 ++++------ share/functions/fish_vi_mode.fish | 3 ++- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/share/functions/fish_vi_key_bindings.fish b/share/functions/fish_vi_key_bindings.fish index bdb2bd8ad..b6b7494e6 100644 --- a/share/functions/fish_vi_key_bindings.fish +++ b/share/functions/fish_vi_key_bindings.fish @@ -5,12 +5,10 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' set fish_key_bindings fish_vi_key_bindings # This triggers the handler, which calls us again and ensures the user_key_bindings are executed return end - # The default escape timeout is 300ms. But for users of Vi bindings that can - # be slightly annoying when trying to switch to Vi "normal" mode. Too, - # vi-mode users are unlikely to use escape-as-meta. So set a much shorter - # timeout in this case. - set -q fish_escape_delay_ms - or set -g fish_escape_delay_ms 10 + # The default escape timeout is 300ms. But for users of Vi bindings that can be slightly + # annoying when trying to switch to Vi "normal" mode. So set a shorter timeout in this case + # unless the user has explicitly set the delay. + set -q fish_escape_delay_ms; or set -g fish_escape_delay_ms 100 set -l init_mode insert set -l eol_keys \$ g\$ \e\[F diff --git a/share/functions/fish_vi_mode.fish b/share/functions/fish_vi_mode.fish index 29ddeb6c9..49023055e 100644 --- a/share/functions/fish_vi_mode.fish +++ b/share/functions/fish_vi_mode.fish @@ -1,5 +1,6 @@ function fish_vi_mode - echo "This function is deprecated. Please call fish_vi_key_bindings directly" >&2 + echo 'The `fish_vi_mode` function is deprecated.' >&2 + echo 'Please switch to calling `fish_vi_key_bindings`.' >&2 # Turn on vi keybindings set -g fish_key_bindings fish_vi_key_bindings end From 5092904ea3a95c6ba78350c3b3761511f613fde8 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Thu, 28 Apr 2016 21:12:58 -0700 Subject: [PATCH 160/363] fix bind unit tests In my rush to get the fix for the wrong default Vi mode escape delay merged (commit 3e24ae80b3739ded7d4066349fc87d13f2b70727) I neglected to update the unit test. This change corrects that oversight. --- tests/bind.expect | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/tests/bind.expect b/tests/bind.expect index 3d49f288f..7589fa8e8 100644 --- a/tests/bind.expect +++ b/tests/bind.expect @@ -60,7 +60,7 @@ expect_prompt -re {\r\nsuccess: default escape timeout\r\n} { # Verify the default timeout has been set to the expected value. send "echo fish_escape_delay_ms=\$fish_escape_delay_ms\r" -expect_prompt -re {\r\nfish_escape_delay_ms=10\r\n} { +expect_prompt -re {\r\nfish_escape_delay_ms=100\r\n} { puts "vi-mode default timeout set correctly" } unmatched { puts stderr "vi-mode default timeout not set correctly" @@ -71,7 +71,7 @@ send "\033" # Delay needed to allow fish to transition to vi "normal" mode. The delay is # longer than strictly necessary to let fish catch up as it may be slow due to # ASAN. -sleep 0.100 +sleep 0.150 send "ddi" send "echo success: default escape timeout\r" expect_prompt -re {\r\nsuccess: default escape timeout\r\n} { @@ -84,7 +84,7 @@ expect_prompt -re {\r\nsuccess: default escape timeout\r\n} { # binding but should be valid while in vi insert or normal mode). send "echo abc def" send "\033" -sleep 0.005 +sleep 0.010 send "t\r" expect_prompt -re {\r\ndef abc\r\n} { puts "vi transpose words, default timeout: short delay" @@ -96,7 +96,7 @@ expect_prompt -re {\r\ndef abc\r\n} { send "echo TEXT" send "\033" # Delay needed to allow fish to transition to vi "normal" mode. -sleep 0.100 +sleep 0.150 send "hhrAi\r" expect_prompt -re {\r\nTAXT\r\n} { puts "vi mode replace char, default timeout: long delay" @@ -105,7 +105,7 @@ expect_prompt -re {\r\nTAXT\r\n} { } # Verify that changing the escape timeout has an effect. -send "set -g fish_escape_delay_ms 100\r" +send "set -g fish_escape_delay_ms 200\r" expect_prompt send "echo fail: lengthened escape timeout" @@ -123,7 +123,7 @@ expect_prompt -re {\r\nsuccess: lengthened escape timeout\r\n} { # after sending escape. send "echo fail: no normal mode" send "\033" -sleep 0.050 +sleep 0.100 send "ddi" send "inserted\r" expect_prompt -re {\r\nfail: no normal modediinserted\r\n} { @@ -151,14 +151,18 @@ expect_prompt -re {\r\nTENT\r\n} { send "set -g fish_key_bindings fish_default_key_bindings\r" expect_prompt -# Verify the custom escape timeout of 100ms set earlier is still in effect. +# Verify the custom escape timeout of 200ms set earlier is still in effect. send "echo fish_escape_delay_ms=\$fish_escape_delay_ms\r" -expect_prompt -re {\r\nfish_escape_delay_ms=100\r\n} { +expect_prompt -re {\r\nfish_escape_delay_ms=200\r\n} { puts "default-mode custom timeout set correctly" } unmatched { puts stderr "default-mode custom timeout not set correctly" } +# Reset it to 100ms. +send "set -g fish_escape_delay_ms 100\r" +expect_prompt + # Verify the emacs transpose word (\et) behavior using various delays, # including none, after the escape character. From 8fc60117419a3f7e98057e7ef1e1f7a2e290f0a8 Mon Sep 17 00:00:00 2001 From: Sanne Wouda Date: Fri, 29 Apr 2016 11:42:15 +0100 Subject: [PATCH 161/363] git takes --help even when it needs a command (#2984) `git --help` is a valid command and fish should complete it as such --- share/completions/git.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index af9dc685d..becc984c2 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -172,7 +172,7 @@ function __fish_git_custom_commands end # general options -complete -f -c git -n 'not __fish_git_needs_command' -l help -d 'Display the manual of a git command' +complete -f -c git -l help -d 'Display the manual of a git command' #### fetch complete -f -c git -n '__fish_git_needs_command' -a fetch -d 'Download objects and refs from another repository' From 6466ffe82da80d0234da68d3ab4cd9a9d3ac42b5 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 28 Apr 2016 21:30:26 +0200 Subject: [PATCH 162/363] git completion: Only show unmerged branches for cherry-pick --- share/completions/git.fish | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index 82ea0e8bf..84329312c 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -11,13 +11,13 @@ function __fish_git_commits end function __fish_git_branches - command git branch --no-color -a ^/dev/null | string match -r -v ' -> ' | string trim -c "* " | string replace -r "^remotes/" "" + command git branch --no-color -a $argv ^/dev/null | string match -r -v ' -> ' | string trim -c "* " | string replace -r "^remotes/" "" end function __fish_git_unique_remote_branches # Allow all remote branches with one remote without the remote part # This is useful for `git checkout` to automatically create a remote-tracking branch - command git branch --no-color -a ^/dev/null | string match -r -v ' -> ' | string trim -c "* " | string replace -r "^remotes/[^/]*/" "" | sort | uniq -u + command git branch --no-color -a $argv ^/dev/null | string match -r -v ' -> ' | string trim -c "* " | string replace -r "^remotes/[^/]*/" "" | sort | uniq -u end function __fish_git_tags @@ -292,8 +292,8 @@ complete -f -c git -n '__fish_git_using_command branch' -l no-merged -d 'List br ### cherry-pick complete -f -c git -n '__fish_git_needs_command' -a cherry-pick -d 'Apply the change introduced by an existing commit' -complete -f -c git -n '__fish_git_using_command cherry-pick' -a '(__fish_git_branches)' -d 'Branch' -complete -f -c git -n '__fish_git_using_command cherry-pick' -a '(__fish_git_unique_remote_branches)' -d 'Remote branch' +complete -f -c git -n '__fish_git_using_command cherry-pick' -a '(__fish_git_branches --no-merged)' -d 'Branch' +complete -f -c git -n '__fish_git_using_command cherry-pick' -a '(__fish_git_unique_remote_branches --no-merged)' -d 'Remote branch' complete -f -c git -n '__fish_git_using_command cherry-pick' -s e -l edit -d 'Edit the commit message prior to committing' complete -f -c git -n '__fish_git_using_command cherry-pick' -s x -d 'Append info in generated commit on the origin of the cherry-picked change' complete -f -c git -n '__fish_git_using_command cherry-pick' -s n -l no-commit -d 'Apply changes without making any commit' From 09bb71398938ffdc4b46767de7bf8cc18ac3cda1 Mon Sep 17 00:00:00 2001 From: Sanne Wouda Date: Fri, 29 Apr 2016 11:42:15 +0100 Subject: [PATCH 163/363] git takes --help even when it needs a command (#2984) `git --help` is a valid command and fish should complete it as such --- share/completions/git.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index 84329312c..a03907052 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -172,7 +172,7 @@ function __fish_git_custom_commands end # general options -complete -f -c git -n 'not __fish_git_needs_command' -l help -d 'Display the manual of a git command' +complete -f -c git -l help -d 'Display the manual of a git command' #### fetch complete -f -c git -n '__fish_git_needs_command' -a fetch -d 'Download objects and refs from another repository' From 85b136314b468f28651db47ca20d277e6ecbf89e Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Fri, 29 Apr 2016 11:13:26 -0700 Subject: [PATCH 164/363] Fix the Xcode build Add missing files to link phase --- fish.xcodeproj/project.pbxproj | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/fish.xcodeproj/project.pbxproj b/fish.xcodeproj/project.pbxproj index b106ec40c..fbb0163e5 100644 --- a/fish.xcodeproj/project.pbxproj +++ b/fish.xcodeproj/project.pbxproj @@ -112,6 +112,20 @@ D0076943199013B900CA4627 /* fish_tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854113B3ACEE0099B651 /* fish_tests.cpp */; }; D00F63F119137E9D00FCCDEC /* fish_version.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D00F63F019137E9D00FCCDEC /* fish_version.cpp */; settings = {COMPILER_FLAGS = "-I$(DERIVED_FILE_DIR)"; }; }; D00F63F219137E9D00FCCDEC /* fish_version.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D00F63F019137E9D00FCCDEC /* fish_version.cpp */; settings = {COMPILER_FLAGS = "-I$(DERIVED_FILE_DIR)"; }; }; + D01243591CD3DAD100C64313 /* builtin_commandline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853013B3ACEE0099B651 /* builtin_commandline.cpp */; }; + D012435A1CD3DAD100C64313 /* builtin_complete.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853113B3ACEE0099B651 /* builtin_complete.cpp */; }; + D012435B1CD3DAD100C64313 /* builtin_jobs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853213B3ACEE0099B651 /* builtin_jobs.cpp */; }; + D012435C1CD3DAD100C64313 /* builtin_set.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853313B3ACEE0099B651 /* builtin_set.cpp */; }; + D012435D1CD3DAD100C64313 /* builtin_set_color.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0C861EA16CC7054003B5A04 /* builtin_set_color.cpp */; }; + D012435E1CD3DAD100C64313 /* builtin_ulimit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853413B3ACEE0099B651 /* builtin_ulimit.cpp */; }; + D012435F1CD3DAD100C64313 /* builtin_printf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0CA63F316FC275F00093BD4 /* builtin_printf.cpp */; }; + D01243601CD3DAE200C64313 /* builtin_commandline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853013B3ACEE0099B651 /* builtin_commandline.cpp */; }; + D01243611CD3DAE200C64313 /* builtin_complete.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853113B3ACEE0099B651 /* builtin_complete.cpp */; }; + D01243621CD3DAE200C64313 /* builtin_jobs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853213B3ACEE0099B651 /* builtin_jobs.cpp */; }; + D01243631CD3DAE200C64313 /* builtin_set.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853313B3ACEE0099B651 /* builtin_set.cpp */; }; + D01243641CD3DAE200C64313 /* builtin_set_color.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0C861EA16CC7054003B5A04 /* builtin_set_color.cpp */; }; + D01243651CD3DAE200C64313 /* builtin_ulimit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853413B3ACEE0099B651 /* builtin_ulimit.cpp */; }; + D01243661CD3DAE200C64313 /* builtin_printf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0CA63F316FC275F00093BD4 /* builtin_printf.cpp */; }; D01A2D24169B736200767098 /* man1 in Copy Files */ = {isa = PBXBuildFile; fileRef = D01A2D23169B730A00767098 /* man1 */; }; D01A2D25169B737700767098 /* man1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = D01A2D23169B730A00767098 /* man1 */; }; D030FBEF1A4A382000F7ADA0 /* input.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854A13B3ACEE0099B651 /* input.cpp */; }; @@ -1298,8 +1312,15 @@ files = ( D030FBF41A4A38F300F7ADA0 /* autoload.cpp in Sources */, D030FBF51A4A38F300F7ADA0 /* builtin.cpp in Sources */, - D04F7FF01BA4E5B900B0F227 /* builtin_string.cpp in Sources */, + D01243591CD3DAD100C64313 /* builtin_commandline.cpp in Sources */, + D012435A1CD3DAD100C64313 /* builtin_complete.cpp in Sources */, + D012435B1CD3DAD100C64313 /* builtin_jobs.cpp in Sources */, + D012435C1CD3DAD100C64313 /* builtin_set.cpp in Sources */, + D012435D1CD3DAD100C64313 /* builtin_set_color.cpp in Sources */, + D012435E1CD3DAD100C64313 /* builtin_ulimit.cpp in Sources */, D030FC151A4A391900F7ADA0 /* builtin_test.cpp in Sources */, + D012435F1CD3DAD100C64313 /* builtin_printf.cpp in Sources */, + D04F7FF01BA4E5B900B0F227 /* builtin_string.cpp in Sources */, D030FBF61A4A38F300F7ADA0 /* color.cpp in Sources */, D0D02AD81598649E008E62BD /* common.cpp in Sources */, D030FBF71A4A38F300F7ADA0 /* complete.cpp in Sources */, @@ -1349,7 +1370,15 @@ buildActionMask = 2147483647; files = ( D0D02A7C159839D5008E62BD /* autoload.cpp in Sources */, + D01243601CD3DAE200C64313 /* builtin_commandline.cpp in Sources */, + D01243611CD3DAE200C64313 /* builtin_complete.cpp in Sources */, + D01243621CD3DAE200C64313 /* builtin_jobs.cpp in Sources */, + D01243631CD3DAE200C64313 /* builtin_set.cpp in Sources */, + D01243641CD3DAE200C64313 /* builtin_set_color.cpp in Sources */, + D01243651CD3DAE200C64313 /* builtin_ulimit.cpp in Sources */, D0D02A7D159839D5008E62BD /* builtin_test.cpp in Sources */, + D01243661CD3DAE200C64313 /* builtin_printf.cpp in Sources */, + D04F7F7C1BA4BF4000B0F227 /* builtin_string.cpp in Sources */, D0D02A7E159839D5008E62BD /* color.cpp in Sources */, D0D02A7F159839D5008E62BD /* common.cpp in Sources */, D0D02A80159839D5008E62BD /* event.cpp in Sources */, @@ -1384,7 +1413,6 @@ D0D02A751598385E008E62BD /* wgetopt.cpp in Sources */, D0D02A7615983869008E62BD /* wutil.cpp in Sources */, D0D02A7715983875008E62BD /* input.cpp in Sources */, - D04F7F7C1BA4BF4000B0F227 /* builtin_string.cpp in Sources */, D0D02A781598387E008E62BD /* output.cpp in Sources */, D0D02A7915983888008E62BD /* intern.cpp in Sources */, D0D02A7B15983928008E62BD /* env_universal_common.cpp in Sources */, From 74d3aa582bb83c8a44e9989810e4b3eb03e1e11f Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Fri, 29 Apr 2016 12:14:10 -0700 Subject: [PATCH 165/363] Issue a \r in fish_title, except when executing it for the prompt fish_title currently outputs some escaped text, which can confuse the line driver (#2453). Issue a carriage return so the line driver knows we are at the beginning of the line, unless we are writing the title as part of the prompt. In that case, we may have text from the previous command still on the line and we don't want to move the cursor. Fixes #2453 --- src/reader.cpp | 12 +++++++++--- src/reader.h | 3 ++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/reader.cpp b/src/reader.cpp index 3254b905d..fb7cc5f28 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -871,7 +871,7 @@ bool reader_thread_job_is_stale() return (void*)(uintptr_t) s_generation_count != pthread_getspecific(generation_count_key); } -void reader_write_title(const wcstring &cmd) +void reader_write_title(const wcstring &cmd, bool reset_cursor_position) { const env_var_t term_str = env_get_string(L"TERM"); @@ -942,6 +942,10 @@ void reader_write_title(const wcstring &cmd) } proc_pop_interactive(); set_color(rgb_color_t::reset(), rgb_color_t::reset()); + if (reset_cursor_position && ! lst.empty()) { + // Put the cursor back at the beginning of the line #2453 + writestr(L"\r"); + } } /** @@ -1000,8 +1004,10 @@ static void exec_prompt() proc_pop_interactive(); } - /* Write the screen title */ - reader_write_title(L""); + // Write the screen title. + // Do not reset the cursor position: exec_prompt is called when there may still be output + // on the line from the previous command (#2499) and we need our PROMPT_SP hack to work + reader_write_title(L"", false); } void reader_init() diff --git a/src/reader.h b/src/reader.h index 0aa105164..874b4711e 100644 --- a/src/reader.h +++ b/src/reader.h @@ -119,8 +119,9 @@ void reader_pop_current_filename(); finishes. \param cmd Command line string passed to \c fish_title if is defined. + \param reset_cursor_position If set, issue a \r so the line driver knows where we are */ -void reader_write_title(const wcstring &cmd); +void reader_write_title(const wcstring &cmd, bool reset_cursor_position = true); /** Call this function to tell the reader that a repaint is needed, and From 8e19c82e09671a11aad0265e4352bce4d73cf420 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Fri, 29 Apr 2016 12:14:10 -0700 Subject: [PATCH 166/363] Issue a \r in fish_title, except when executing it for the prompt fish_title currently outputs some escaped text, which can confuse the line driver (#2453). Issue a carriage return so the line driver knows we are at the beginning of the line, unless we are writing the title as part of the prompt. In that case, we may have text from the previous command still on the line and we don't want to move the cursor. Fixes #2453 --- src/reader.cpp | 12 +++++++++--- src/reader.h | 3 ++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/reader.cpp b/src/reader.cpp index 50c8c93d4..18f2a7311 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -873,7 +873,7 @@ bool reader_thread_job_is_stale() return (void*)(uintptr_t) s_generation_count != pthread_getspecific(generation_count_key); } -void reader_write_title(const wcstring &cmd) +void reader_write_title(const wcstring &cmd, bool reset_cursor_position) { const env_var_t term_str = env_get_string(L"TERM"); @@ -944,6 +944,10 @@ void reader_write_title(const wcstring &cmd) } proc_pop_interactive(); set_color(rgb_color_t::reset(), rgb_color_t::reset()); + if (reset_cursor_position && ! lst.empty()) { + // Put the cursor back at the beginning of the line #2453 + writestr(L"\r"); + } } /** @@ -1002,8 +1006,10 @@ static void exec_prompt() proc_pop_interactive(); } - /* Write the screen title */ - reader_write_title(L""); + // Write the screen title. + // Do not reset the cursor position: exec_prompt is called when there may still be output + // on the line from the previous command (#2499) and we need our PROMPT_SP hack to work + reader_write_title(L"", false); } void reader_init() diff --git a/src/reader.h b/src/reader.h index 3d9208a0a..67ba1a2e5 100644 --- a/src/reader.h +++ b/src/reader.h @@ -117,8 +117,9 @@ void reader_pop_current_filename(); finishes. \param cmd Command line string passed to \c fish_title if is defined. + \param reset_cursor_position If set, issue a \r so the line driver knows where we are */ -void reader_write_title(const wcstring &cmd); +void reader_write_title(const wcstring &cmd, bool reset_cursor_position = true); /** Call this function to tell the reader that a repaint is needed, and From 4c84224d06df694dd1f40a9e5091576795b2b7f5 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Fri, 29 Apr 2016 13:58:21 -0700 Subject: [PATCH 167/363] Add some more files missing from Xcode compile step Previously the .cpp files were #included by common.cpp Now they get compiled separately --- fish.xcodeproj/project.pbxproj | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fish.xcodeproj/project.pbxproj b/fish.xcodeproj/project.pbxproj index fbb0163e5..cf22a7e56 100644 --- a/fish.xcodeproj/project.pbxproj +++ b/fish.xcodeproj/project.pbxproj @@ -126,6 +126,10 @@ D01243641CD3DAE200C64313 /* builtin_set_color.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0C861EA16CC7054003B5A04 /* builtin_set_color.cpp */; }; D01243651CD3DAE200C64313 /* builtin_ulimit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853413B3ACEE0099B651 /* builtin_ulimit.cpp */; }; D01243661CD3DAE200C64313 /* builtin_printf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0CA63F316FC275F00093BD4 /* builtin_printf.cpp */; }; + D01243681CD4015600C64313 /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855E13B3ACEE0099B651 /* util.cpp */; }; + D01243691CD4015C00C64313 /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855E13B3ACEE0099B651 /* util.cpp */; }; + D012436A1CD4018100C64313 /* fallback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853E13B3ACEE0099B651 /* fallback.cpp */; }; + D012436B1CD4019700C64313 /* fallback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853E13B3ACEE0099B651 /* fallback.cpp */; }; D01A2D24169B736200767098 /* man1 in Copy Files */ = {isa = PBXBuildFile; fileRef = D01A2D23169B730A00767098 /* man1 */; }; D01A2D25169B737700767098 /* man1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = D01A2D23169B730A00767098 /* man1 */; }; D030FBEF1A4A382000F7ADA0 /* input.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854A13B3ACEE0099B651 /* input.cpp */; }; @@ -1332,6 +1336,7 @@ D030FBFC1A4A38F300F7ADA0 /* parse_productions.cpp in Sources */, D030FBFD1A4A38F300F7ADA0 /* parse_tree.cpp in Sources */, D030FBFE1A4A38F300F7ADA0 /* parse_execution.cpp in Sources */, + D012436B1CD4019700C64313 /* fallback.cpp in Sources */, D030FC001A4A38F300F7ADA0 /* function.cpp in Sources */, D030FC011A4A38F300F7ADA0 /* highlight.cpp in Sources */, D030FC021A4A38F300F7ADA0 /* history.cpp in Sources */, @@ -1360,6 +1365,7 @@ D030FC131A4A38F300F7ADA0 /* wgetopt.cpp in Sources */, D030FC141A4A38F300F7ADA0 /* wildcard.cpp in Sources */, D0D02ADA159864AB008E62BD /* wutil.cpp in Sources */, + D01243691CD4015C00C64313 /* util.cpp in Sources */, D0D02AD615986492008E62BD /* fish_indent.cpp in Sources */, D00F63F219137E9D00FCCDEC /* fish_version.cpp in Sources */, ); @@ -1399,6 +1405,7 @@ D0D02A6A1598381A008E62BD /* exec.cpp in Sources */, D0F5B46519CFCDE80090665E /* wcstringutil.cpp in Sources */, D0D02A6B1598381F008E62BD /* expand.cpp in Sources */, + D012436A1CD4018100C64313 /* fallback.cpp in Sources */, D00F63F119137E9D00FCCDEC /* fish_version.cpp in Sources */, D0D02A6C15983829008E62BD /* highlight.cpp in Sources */, D0D02A6D1598382C008E62BD /* history.cpp in Sources */, @@ -1420,6 +1427,7 @@ D0D02A89159839DF008E62BD /* fish.cpp in Sources */, D0C52F371765284C00BFAB82 /* parse_tree.cpp in Sources */, D0FE8EE8179FB760008C9F21 /* parse_productions.cpp in Sources */, + D01243681CD4015600C64313 /* util.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; From bd2b107d374c095d8d5ced7a7b5ed78b1381e062 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Fri, 29 Apr 2016 14:54:21 -0700 Subject: [PATCH 168/363] Remove some unused macros from builtin_set_color.cpp --- src/builtin_set_color.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/builtin_set_color.cpp b/src/builtin_set_color.cpp index 2cd1d876f..659e6536b 100644 --- a/src/builtin_set_color.cpp +++ b/src/builtin_set_color.cpp @@ -32,16 +32,6 @@ class parser_t; -/// Error message for invalid path operations. -#define BUILTIN_SET_PATH_ERROR L"%ls: Warning: path component %ls may not be valid in %ls.\n" - -/// Hint for invalid path operation with a colon. -#define BUILTIN_SET_PATH_HINT L"%ls: Did you mean 'set %ls $%ls %ls'?\n" - -/// Error for mismatch between index count and elements. -#define BUILTIN_SET_ARG_COUNT \ - L"%ls: The number of variable indexes does not match the number of values\n" - static void print_colors(io_streams_t &streams) { const wcstring_list_t result = rgb_color_t::named_color_names(); size_t i; From ba5a55b754d24270222380c1f5f722dc8b4dc4c5 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Fri, 29 Apr 2016 14:55:23 -0700 Subject: [PATCH 169/363] Remove an errant newline in the fish_cancel_commandline output Now the next line appears immediately after the cancelled line, without an intervening newline --- share/functions/__fish_cancel_commandline.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/functions/__fish_cancel_commandline.fish b/share/functions/__fish_cancel_commandline.fish index f10be75fd..5117e738a 100644 --- a/share/functions/__fish_cancel_commandline.fish +++ b/share/functions/__fish_cancel_commandline.fish @@ -3,7 +3,7 @@ function __fish_cancel_commandline set -l cmd (commandline) if test -n "$cmd" commandline -C 1000000 - echo (set_color -b bryellow black)"^C"(set_color normal) + echo -n (set_color -b bryellow black)"^C"(set_color normal) for i in (seq (commandline -L)) echo "" end From b064da8d38370af43d9b859087b8d919bdf40d5e Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Fri, 29 Apr 2016 15:33:46 -0700 Subject: [PATCH 170/363] Erase the autosuggestion in fish_cancel_commandline by clearing to EOL --- share/functions/__fish_cancel_commandline.fish | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/share/functions/__fish_cancel_commandline.fish b/share/functions/__fish_cancel_commandline.fish index 5117e738a..959e6800b 100644 --- a/share/functions/__fish_cancel_commandline.fish +++ b/share/functions/__fish_cancel_commandline.fish @@ -3,7 +3,8 @@ function __fish_cancel_commandline set -l cmd (commandline) if test -n "$cmd" commandline -C 1000000 - echo -n (set_color -b bryellow black)"^C"(set_color normal) + # set a color, output ^C, restore color, clear to EOL (to erase any autosuggestion) + echo -n (set_color -b bryellow black)"^C"(set_color normal)\033"[0K" for i in (seq (commandline -L)) echo "" end From 54319435f43add1c5001e26ae9b505946f64950d Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Thu, 28 Apr 2016 20:53:09 -0700 Subject: [PATCH 171/363] clarify fish_vi_mode deprecation warning Also, correct the Vi mode default escape timeout. I intended it to be 100 ms in my previous change but it ended up 10 ms which is far too short. A 10 ms delay will continue to cause problems for people running fish inside `screen`, `tmux`, or over high latency connections. Cherry-picked from 3e24ae80b3739ded7d4066349fc87d13f2b70727 --- share/functions/fish_vi_key_bindings.fish | 17 +++++++++++------ share/functions/fish_vi_mode.fish | 10 ++++------ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/share/functions/fish_vi_key_bindings.fish b/share/functions/fish_vi_key_bindings.fish index 2518ab74c..edff29f88 100644 --- a/share/functions/fish_vi_key_bindings.fish +++ b/share/functions/fish_vi_key_bindings.fish @@ -1,10 +1,15 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' - # The default escape timeout is 300ms. But for users of Vi bindings that can - # be slightly annoying when trying to switch to Vi "normal" mode. Too, - # vi-mode users are unlikely to use escape-as-meta. So set a much shorter - # timeout in this case. - set -q fish_escape_delay_ms - or set -g fish_escape_delay_ms 10 + if test "$fish_key_bindings" != "fish_vi_key_bindings" + # Allow the user to set the variable universally + set -q fish_key_bindings; or set -g fish_key_bindings + set fish_key_bindings fish_vi_key_bindings # This triggers the handler, which calls us again and ensures the user_key_bindings are executed + return + end + + # The default escape timeout is 300ms. But for users of Vi bindings that can be slightly + # annoying when trying to switch to Vi "normal" mode. So set a shorter timeout in this case + # unless the user has explicitly set the delay. + set -q fish_escape_delay_ms; or set -g fish_escape_delay_ms 100 set -l init_mode insert set -l eol_keys \$ g\$ \e\[F diff --git a/share/functions/fish_vi_mode.fish b/share/functions/fish_vi_mode.fish index b4fbe63a7..4b54dd00f 100644 --- a/share/functions/fish_vi_mode.fish +++ b/share/functions/fish_vi_mode.fish @@ -1,8 +1,6 @@ function fish_vi_mode - # Set the __fish_vi_mode variable - # This triggers fish_mode_prompt to output the mode indicator - set -g __fish_vi_mode 1 - - # Turn on vi keybindings - set -g fish_key_bindings fish_vi_key_bindings + echo 'The `fish_vi_mode` function is deprecated.' >&2 + echo 'Please switch to calling `fish_vi_key_bindings`.' >&2 + # Turn on vi keybindings + set -g fish_key_bindings fish_vi_key_bindings end From f44ef3ad3f713bfbd777fdb1822f1296f6218c35 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Thu, 28 Apr 2016 21:12:58 -0700 Subject: [PATCH 172/363] fix bind unit tests In my rush to get the fix for the wrong default Vi mode escape delay merged (commit 3e24ae80b3739ded7d4066349fc87d13f2b70727) I neglected to update the unit test. This change corrects that oversight. Cherry-picked from 5092904ea3a95c6ba78350c3b3761511f613fde8 --- tests/bind.expect | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/tests/bind.expect b/tests/bind.expect index 3d49f288f..7589fa8e8 100644 --- a/tests/bind.expect +++ b/tests/bind.expect @@ -60,7 +60,7 @@ expect_prompt -re {\r\nsuccess: default escape timeout\r\n} { # Verify the default timeout has been set to the expected value. send "echo fish_escape_delay_ms=\$fish_escape_delay_ms\r" -expect_prompt -re {\r\nfish_escape_delay_ms=10\r\n} { +expect_prompt -re {\r\nfish_escape_delay_ms=100\r\n} { puts "vi-mode default timeout set correctly" } unmatched { puts stderr "vi-mode default timeout not set correctly" @@ -71,7 +71,7 @@ send "\033" # Delay needed to allow fish to transition to vi "normal" mode. The delay is # longer than strictly necessary to let fish catch up as it may be slow due to # ASAN. -sleep 0.100 +sleep 0.150 send "ddi" send "echo success: default escape timeout\r" expect_prompt -re {\r\nsuccess: default escape timeout\r\n} { @@ -84,7 +84,7 @@ expect_prompt -re {\r\nsuccess: default escape timeout\r\n} { # binding but should be valid while in vi insert or normal mode). send "echo abc def" send "\033" -sleep 0.005 +sleep 0.010 send "t\r" expect_prompt -re {\r\ndef abc\r\n} { puts "vi transpose words, default timeout: short delay" @@ -96,7 +96,7 @@ expect_prompt -re {\r\ndef abc\r\n} { send "echo TEXT" send "\033" # Delay needed to allow fish to transition to vi "normal" mode. -sleep 0.100 +sleep 0.150 send "hhrAi\r" expect_prompt -re {\r\nTAXT\r\n} { puts "vi mode replace char, default timeout: long delay" @@ -105,7 +105,7 @@ expect_prompt -re {\r\nTAXT\r\n} { } # Verify that changing the escape timeout has an effect. -send "set -g fish_escape_delay_ms 100\r" +send "set -g fish_escape_delay_ms 200\r" expect_prompt send "echo fail: lengthened escape timeout" @@ -123,7 +123,7 @@ expect_prompt -re {\r\nsuccess: lengthened escape timeout\r\n} { # after sending escape. send "echo fail: no normal mode" send "\033" -sleep 0.050 +sleep 0.100 send "ddi" send "inserted\r" expect_prompt -re {\r\nfail: no normal modediinserted\r\n} { @@ -151,14 +151,18 @@ expect_prompt -re {\r\nTENT\r\n} { send "set -g fish_key_bindings fish_default_key_bindings\r" expect_prompt -# Verify the custom escape timeout of 100ms set earlier is still in effect. +# Verify the custom escape timeout of 200ms set earlier is still in effect. send "echo fish_escape_delay_ms=\$fish_escape_delay_ms\r" -expect_prompt -re {\r\nfish_escape_delay_ms=100\r\n} { +expect_prompt -re {\r\nfish_escape_delay_ms=200\r\n} { puts "default-mode custom timeout set correctly" } unmatched { puts stderr "default-mode custom timeout not set correctly" } +# Reset it to 100ms. +send "set -g fish_escape_delay_ms 100\r" +expect_prompt + # Verify the emacs transpose word (\et) behavior using various delays, # including none, after the escape character. From 52731c480c789ae34e7e9bd2233b0440fab328b8 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Wed, 13 Apr 2016 17:14:50 -0700 Subject: [PATCH 173/363] provide a realpath implementation Not all distros have a `realpath` command. Provide a function that uses the real command if available else use the fish builtin. Fixes #2932 (cherry picked from commit 6c329e8a839c1b5eeaf2e545b4f4084c3a8830f7) --- doc_src/fish_realpath.txt | 13 +++ share/functions/realpath.fish | 13 +++ src/builtin.cpp | 147 ++++++++++++++++++++-------------- tests/fish_realpath.err | 3 + tests/fish_realpath.in | 39 +++++++++ tests/fish_realpath.out | 5 ++ tests/fish_realpath.status | 1 + 7 files changed, 160 insertions(+), 61 deletions(-) create mode 100644 doc_src/fish_realpath.txt create mode 100644 share/functions/realpath.fish create mode 100644 tests/fish_realpath.err create mode 100644 tests/fish_realpath.in create mode 100644 tests/fish_realpath.out create mode 100644 tests/fish_realpath.status diff --git a/doc_src/fish_realpath.txt b/doc_src/fish_realpath.txt new file mode 100644 index 000000000..a2ba0d0f9 --- /dev/null +++ b/doc_src/fish_realpath.txt @@ -0,0 +1,13 @@ +\section fish_realpath fish_realpath - Convert a path to an absolute path without symlinks + +\subsection fish_realpath-synopsis Synopsis +\fish{synopsis} +fish_realpath path +\endfish + +\subsection fish_realpath-description Description + +This is an implementation of the external realpath command that doesn't support any options. It's meant to be used only by scripts which need to be portable. In general scripts shouldn't invoke this directly. They should just use `realpath` which will fallback to this builtin if an external command cannot be found. + +If the path is invalid no translated path will be written to stdout and an error will be reported. +This implementation behaves like the GNU command being invoked with `--canonicalize-existing`. diff --git a/share/functions/realpath.fish b/share/functions/realpath.fish new file mode 100644 index 000000000..3d33ae0f3 --- /dev/null +++ b/share/functions/realpath.fish @@ -0,0 +1,13 @@ +# Provide a minimalist realpath implementation to help deal with platforms that may not provide it +# as a command. If a realpath command is available simply pass all arguments thru to it. If not +# fallback to alternative solutions. + +# The following is slightly subtle. The first time `realpath` is invoked this script will be read. +# If we see that there is an external command by that name we just return. That will cause fish to +# run the external command. On the other hand, if an external command isn't found we define a +# function that will provide fallback behavior. +if not type -q -P realpath + function realpath --description 'fallback realpath implementation' + builtin fish_realpath $argv[-1] + end +end diff --git a/src/builtin.cpp b/src/builtin.cpp index 86213a762..6a1bcc4d4 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -3980,72 +3980,97 @@ int builtin_false(parser_t &parser, io_streams_t &streams, wchar_t **argv) return STATUS_BUILTIN_ERROR; } -/* - END OF BUILTIN COMMANDS - Below are functions for handling the builtin commands. - THESE MUST BE SORTED BY NAME! Completion lookup uses binary search. -*/ +/// An implementation of the external realpath command that doesn't support any options. It's meant +/// to be used only by scripts which need to be portable. In general scripts shouldn't invoke this +/// directly. They should just use `realpath` which will fallback to this builtin if an external +/// command cannot be found. This behaves like the external `realpath --canonicalize-existing`; +/// that is, it requires all path components, including the final, to exist. +int builtin_fish_realpath(parser_t &parser, io_streams_t &streams, wchar_t **argv) { + int argc = builtin_count_args(argv); -/** - Data about all the builtin commands in fish. - Functions that are bound to builtin_generic are handled directly by the parser. - NOTE: These must be kept in sorted order! -*/ -static const builtin_data_t builtin_datas[]= -{ - { L"[", &builtin_test, N_(L"Test a condition") }, + if (argc != 2) { + streams.err.append_format(_(L"%ls: Expected one argument, got %d\n"), argv[0], argc - 1); + return STATUS_BUILTIN_ERROR; + } + + wchar_t *real_path = wrealpath(argv[1], NULL); + if (real_path) { + // Yay! We could resolve the path. + streams.out.append(real_path); + free((void *)real_path); + } else { + // The path isn't a simple filename and couldn't be resolved to an absolute path. + streams.err.append_format(_(L"%ls: Invalid path: %ls\n"), argv[0], argv[1]); + return STATUS_BUILTIN_ERROR; + } + streams.out.append(L"\n"); + return STATUS_BUILTIN_OK; +} + +// END OF BUILTIN COMMANDS +// Below are functions for handling the builtin commands. +// THESE MUST BE SORTED BY NAME! Completion lookup uses binary search. + +// Data about all the builtin commands in fish. +// Functions that are bound to builtin_generic are handled directly by the parser. +// NOTE: These must be kept in sorted order! +static const builtin_data_t builtin_datas[] = { + {L"[", &builtin_test, N_(L"Test a condition")}, #if 0 // Disabled for the 2.2.0 release: https://github.com/fish-shell/fish-shell/issues/1809. { L"__fish_parse", &builtin_parse, N_(L"Try out the new parser") }, #endif - { L"and", &builtin_generic, N_(L"Execute command if previous command suceeded") }, - { L"begin", &builtin_generic, 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_generic, N_(L"Conditionally execute a block of commands") }, - { L"cd", &builtin_cd, N_(L"Change working directory") }, - { L"command", &builtin_command, 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_generic, N_(L"Evaluate block if condition is false") }, - { L"emit", &builtin_emit, N_(L"Emit an event") }, - { L"end", &builtin_generic, 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"false", &builtin_false, N_(L"Return an unsuccessful result") }, - { L"fg", &builtin_fg, N_(L"Send job to foreground") }, - { L"for", &builtin_generic, N_(L"Perform a set of commands multiple times") }, - { L"function", &builtin_generic, 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"printf", &builtin_printf, N_(L"Prints formatted text") }, - { 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"set_color", &builtin_set_color, N_(L"Set the terminal color") }, - { L"source", &builtin_source, N_(L"Evaluate contents of file") }, - { L"status", &builtin_status, N_(L"Return status information about fish") }, - { L"string", &builtin_string, N_(L"Manipulate strings") }, - { L"switch", &builtin_generic, N_(L"Conditionally execute a block of commands") }, - { L"test", &builtin_test, N_(L"Test a condition") }, - { L"true", &builtin_true, N_(L"Return a successful result") }, - { 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"and", &builtin_generic, N_(L"Execute command if previous command suceeded")}, + {L"begin", &builtin_generic, 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_generic, N_(L"Conditionally execute a block of commands")}, + {L"cd", &builtin_cd, N_(L"Change working directory")}, + {L"command", &builtin_command, 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_generic, N_(L"Evaluate block if condition is false")}, + {L"emit", &builtin_emit, N_(L"Emit an event")}, + {L"end", &builtin_generic, 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"false", &builtin_false, N_(L"Return an unsuccessful result")}, + {L"fg", &builtin_fg, N_(L"Send job to foreground")}, + {L"fish_realpath", &builtin_fish_realpath, + N_(L"Convert path to absolute path without symlinks")}, + {L"for", &builtin_generic, N_(L"Perform a set of commands multiple times")}, + {L"function", &builtin_generic, 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"printf", &builtin_printf, N_(L"Prints formatted text")}, + {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"set_color", &builtin_set_color, N_(L"Set the terminal color")}, + {L"source", &builtin_source, N_(L"Evaluate contents of file")}, + {L"status", &builtin_status, N_(L"Return status information about fish")}, + {L"string", &builtin_string, N_(L"Manipulate strings")}, + {L"switch", &builtin_generic, N_(L"Conditionally execute a block of commands")}, + {L"test", &builtin_test, N_(L"Test a condition")}, + {L"true", &builtin_true, N_(L"Return a successful result")}, + {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) diff --git a/tests/fish_realpath.err b/tests/fish_realpath.err new file mode 100644 index 000000000..f12c36186 --- /dev/null +++ b/tests/fish_realpath.err @@ -0,0 +1,3 @@ +fish_realpath: Invalid path: /this/better/be/an/invalid/path +fish_realpath: Invalid path: nonexistent-file +fish_realpath: Invalid path: ../test/data/fish-symlink/nonexistent-file-relative-to-a-symlink diff --git a/tests/fish_realpath.in b/tests/fish_realpath.in new file mode 100644 index 000000000..f25309c99 --- /dev/null +++ b/tests/fish_realpath.in @@ -0,0 +1,39 @@ +# $XDG_DATA_HOME can itself be a relative path. So force it to an absolute +# path so we can remove it from any resolved paths below. This is needed +# because the contents of the fish_realpath.out file can't include any $PWD +# data since $PWD isn't under our control. +set -l data_home_realpath (fish_realpath $XDG_DATA_HOME) + +# A bogus absolute path is handled correctly and sets a failure status. +if not fish_realpath /this/better/be/an/invalid/path + echo first invalid path handled okay +end + +# A non-existent file relative to $PWD fails. +fish_realpath nonexistent-file + +# The simplest absolute path should undergo no transformation. +fish_realpath / + +# A single symlink to a directory is correctly resolved. +ln -s fish $XDG_DATA_HOME/fish-symlink +set -l real_path (fish_realpath $XDG_DATA_HOME/fish-symlink) +string replace "$data_home_realpath/" "" $real_path + +# A nonexistent file relative to a valid symlink to a directory fails. +# This depends on the symlink created by the previous test. +set -l real_path (fish_realpath $XDG_DATA_HOME/fish-symlink/nonexistent-file-relative-to-a-symlink) + +# A path with two symlinks, first to a directory, second to a file, is correctly resolved. +ln -s fish $XDG_DATA_HOME/fish-symlink2 +touch $XDG_DATA_HOME/fish/real_file +ln -s real_file $XDG_DATA_HOME/fish/symlink_file +set -l real_path (fish_realpath $XDG_DATA_HOME/fish-symlink/symlink_file) +string replace "$data_home_realpath/" "" $real_path + +# The $PWD should undergo no further transformations because it should already +# be a "realpath". +set -l real_path (fish_realpath $PWD) +string replace $PWD "pwd-resolved-to-itself" $real_path + +exit 0 diff --git a/tests/fish_realpath.out b/tests/fish_realpath.out new file mode 100644 index 000000000..d14bca83b --- /dev/null +++ b/tests/fish_realpath.out @@ -0,0 +1,5 @@ +first invalid path handled okay +/ +fish +fish/real_file +pwd-resolved-to-itself diff --git a/tests/fish_realpath.status b/tests/fish_realpath.status new file mode 100644 index 000000000..573541ac9 --- /dev/null +++ b/tests/fish_realpath.status @@ -0,0 +1 @@ +0 From 4bb805b5fe2cda1b1eba769433f06bfb41aa8120 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Fri, 29 Apr 2016 21:47:43 -0700 Subject: [PATCH 174/363] document new `fish_realpath` builtin --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f323ff6dd..26280dc8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - A new `string` builtin to handle... strings! This builtin will measure, split, search and replace text strings, including using regular expressions. It can also be used to turn lists into plain strings using `join`. `string` can be used in place of `sed`, `grep`, `tr`, `cut`, and `awk` in many situations. (#2296) - Allow using escape as the Meta modifier key, by waiting after seeing an escape character wait up to 300ms for an additional character. This is consistent with readline (e.g. bash) and can be configured via the `fish_escape_delay_ms variable`. This allows using escape as the Meta modifier. (#1356) - Add new directories for vendor functions and configuration snippets (#2500) +- A new `fish_realpath` builtin and associated `realpath` function should allow scripts to resolve path names via `realpath` regardless of whether there is an external command of that name; albeit with some limitations. See the associated documentation. # Backward-incompatible changes From b32bf2261617d30019d25d04ce451a29e7297c14 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 30 Apr 2016 16:21:41 +0200 Subject: [PATCH 175/363] Add repository/refspec completion to git A few commands (fetch, pull and push at least) take a "repository" (aka "remote") and then a "refspec" (we currently do branches here). Fixes #2525 (seems that man is still alive) --- share/completions/git.fish | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index becc984c2..1bfe80b80 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -171,12 +171,29 @@ function __fish_git_custom_commands end end +# Suggest branches for the specified remote - returns 1 if no known remote is specified +function __fish_git_branch_for_remote + set -l remotes (__fish_git_remotes) + set -l remote + set -l cmd (commandline -opc) + for r in $remotes + if contains -- $r $cmd + set remote $r + break + end + end + set -q remote[1]; or return 1 + __fish_git_branches | string match -- "$remote/*" | string replace -- "$remote/" '' +end + # general options complete -f -c git -l help -d 'Display the manual of a git command' #### fetch complete -f -c git -n '__fish_git_needs_command' -a fetch -d 'Download objects and refs from another repository' -complete -f -c git -n '__fish_git_using_command fetch' -a '(__fish_git_remotes)' -d 'Remote' +# Suggest "repository", then "refspec" - this also applies to e.g. push/pull +complete -f -c git -n '__fish_git_using_command fetch; and not __fish_git_branch_for_remote' -a '(__fish_git_remotes)' -d 'Remote' +complete -f -c git -n '__fish_git_using_command fetch; and __fish_git_branch_for_remote' -a '(__fish_git_branch_for_remote)' -d 'Branch' complete -f -c git -n '__fish_git_using_command fetch' -s q -l quiet -d 'Be quiet' complete -f -c git -n '__fish_git_using_command fetch' -s v -l verbose -d 'Be verbose' complete -f -c git -n '__fish_git_using_command fetch' -s a -l append -d 'Append ref names and object names' @@ -393,15 +410,14 @@ complete -f -c git -n '__fish_git_using_command pull' -s k -l keep -d 'Keep down complete -f -c git -n '__fish_git_using_command pull' -l no-tags -d 'Disable automatic tag following' # TODO --upload-pack complete -f -c git -n '__fish_git_using_command pull' -l progress -d 'Force progress status' -complete -f -c git -n '__fish_git_using_command pull' -a '(git remote)' -d 'Remote alias' -complete -f -c git -n '__fish_git_using_command pull' -a '(__fish_git_branches)' -d 'Branch' -complete -f -c git -n '__fish_git_using_command pull' -a '(__fish_git_unique_remote_branches)' -d 'Remote branch' +complete -f -c git -n '__fish_git_using_command pull; and not __fish_git_branch_for_remote' -a '(__fish_git_remotes)' -d 'Remote alias' +complete -f -c git -n '__fish_git_using_command pull; and __fish_git_branch_for_remote' -a '(__fish_git_branch_for_remote)' -d 'Branch' # TODO other options ### push complete -f -c git -n '__fish_git_needs_command' -a push -d 'Update remote refs along with associated objects' -complete -f -c git -n '__fish_git_using_command push' -a '(git remote)' -d 'Remote alias' -complete -f -c git -n '__fish_git_using_command push' -a '(__fish_git_branches)' -d 'Branch' +complete -f -c git -n '__fish_git_using_command push; and not __fish_git_branch_for_remote' -a '(__fish_git_remotes)' -d 'Remote alias' +complete -f -c git -n '__fish_git_using_command push; and __fish_git_branch_for_remote' -a '(__fish_git_branches)' -d 'Branch' complete -f -c git -n '__fish_git_using_command push' -l all -d 'Push all refs under refs/heads/' complete -f -c git -n '__fish_git_using_command push' -l prune -d "Remove remote branches that don't have a local counterpart" complete -f -c git -n '__fish_git_using_command push' -l mirror -d 'Push all refs under refs/' @@ -418,7 +434,7 @@ complete -f -c git -n '__fish_git_using_command push' -l progress -d 'Force prog ### rebase complete -f -c git -n '__fish_git_needs_command' -a rebase -d 'Forward-port local commits to the updated upstream head' -complete -f -c git -n '__fish_git_using_command rebase' -a '(git remote)' -d 'Remote alias' +complete -f -c git -n '__fish_git_using_command rebase' -a '(__fish_git_remotes)' -d 'Remote alias' complete -f -c git -n '__fish_git_using_command rebase' -a '(__fish_git_branches)' -d 'Branch' complete -f -c git -n '__fish_git_using_command rebase' -l continue -d 'Restart the rebasing process' complete -f -c git -n '__fish_git_using_command rebase' -l abort -d 'Abort the rebase operation' From fde26d404956f843860f2bf5bb00dd3e1636632a Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 30 Apr 2016 16:30:02 +0200 Subject: [PATCH 176/363] git completion: Allow optional "+" for push This signifies a force-push. To avoid cluttering, only complete branches if a + is already given. Fixes #2879. --- share/completions/git.fish | 3 +++ 1 file changed, 3 insertions(+) diff --git a/share/completions/git.fish b/share/completions/git.fish index 1bfe80b80..d47d059a8 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -417,7 +417,10 @@ complete -f -c git -n '__fish_git_using_command pull; and __fish_git_branch_for_ ### push complete -f -c git -n '__fish_git_needs_command' -a push -d 'Update remote refs along with associated objects' complete -f -c git -n '__fish_git_using_command push; and not __fish_git_branch_for_remote' -a '(__fish_git_remotes)' -d 'Remote alias' +# The "refspec" here is an optional "+" to signify a force-push (implemented) +# then src:dest (where both src and dest are git objects, so we most likely want to complete branches (not implemented) complete -f -c git -n '__fish_git_using_command push; and __fish_git_branch_for_remote' -a '(__fish_git_branches)' -d 'Branch' +complete -f -c git -n '__fish_git_using_command push; and __fish_git_branch_for_remote; and string match -q "+*" -- (commandline -ct)' -a '+(__fish_git_branches)' -d 'Force-push branch' complete -f -c git -n '__fish_git_using_command push' -l all -d 'Push all refs under refs/heads/' complete -f -c git -n '__fish_git_using_command push' -l prune -d "Remove remote branches that don't have a local counterpart" complete -f -c git -n '__fish_git_using_command push' -l mirror -d 'Push all refs under refs/' From 901652d5c37cd60131238fd29af9bc57ec2acdbf Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 30 Apr 2016 16:21:41 +0200 Subject: [PATCH 177/363] Add repository/refspec completion to git A few commands (fetch, pull and push at least) take a "repository" (aka "remote") and then a "refspec" (we currently do branches here). Fixes #2525 (seems that man is still alive) (cherry picked from commit b32bf2261617d30019d25d04ce451a29e7297c14) --- share/completions/git.fish | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index a03907052..a6bce22d0 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -171,12 +171,29 @@ function __fish_git_custom_commands end end +# Suggest branches for the specified remote - returns 1 if no known remote is specified +function __fish_git_branch_for_remote + set -l remotes (__fish_git_remotes) + set -l remote + set -l cmd (commandline -opc) + for r in $remotes + if contains -- $r $cmd + set remote $r + break + end + end + set -q remote[1]; or return 1 + __fish_git_branches | string match -- "$remote/*" | string replace -- "$remote/" '' +end + # general options complete -f -c git -l help -d 'Display the manual of a git command' #### fetch complete -f -c git -n '__fish_git_needs_command' -a fetch -d 'Download objects and refs from another repository' -complete -f -c git -n '__fish_git_using_command fetch' -a '(__fish_git_remotes)' -d 'Remote' +# Suggest "repository", then "refspec" - this also applies to e.g. push/pull +complete -f -c git -n '__fish_git_using_command fetch; and not __fish_git_branch_for_remote' -a '(__fish_git_remotes)' -d 'Remote' +complete -f -c git -n '__fish_git_using_command fetch; and __fish_git_branch_for_remote' -a '(__fish_git_branch_for_remote)' -d 'Branch' complete -f -c git -n '__fish_git_using_command fetch' -s q -l quiet -d 'Be quiet' complete -f -c git -n '__fish_git_using_command fetch' -s v -l verbose -d 'Be verbose' complete -f -c git -n '__fish_git_using_command fetch' -s a -l append -d 'Append ref names and object names' @@ -393,15 +410,14 @@ complete -f -c git -n '__fish_git_using_command pull' -s k -l keep -d 'Keep down complete -f -c git -n '__fish_git_using_command pull' -l no-tags -d 'Disable automatic tag following' # TODO --upload-pack complete -f -c git -n '__fish_git_using_command pull' -l progress -d 'Force progress status' -complete -f -c git -n '__fish_git_using_command pull' -a '(git remote)' -d 'Remote alias' -complete -f -c git -n '__fish_git_using_command pull' -a '(__fish_git_branches)' -d 'Branch' -complete -f -c git -n '__fish_git_using_command pull' -a '(__fish_git_unique_remote_branches)' -d 'Remote branch' +complete -f -c git -n '__fish_git_using_command pull; and not __fish_git_branch_for_remote' -a '(__fish_git_remotes)' -d 'Remote alias' +complete -f -c git -n '__fish_git_using_command pull; and __fish_git_branch_for_remote' -a '(__fish_git_branch_for_remote)' -d 'Branch' # TODO other options ### push complete -f -c git -n '__fish_git_needs_command' -a push -d 'Update remote refs along with associated objects' -complete -f -c git -n '__fish_git_using_command push' -a '(git remote)' -d 'Remote alias' -complete -f -c git -n '__fish_git_using_command push' -a '(__fish_git_branches)' -d 'Branch' +complete -f -c git -n '__fish_git_using_command push; and not __fish_git_branch_for_remote' -a '(__fish_git_remotes)' -d 'Remote alias' +complete -f -c git -n '__fish_git_using_command push; and __fish_git_branch_for_remote' -a '(__fish_git_branches)' -d 'Branch' complete -f -c git -n '__fish_git_using_command push' -l all -d 'Push all refs under refs/heads/' complete -f -c git -n '__fish_git_using_command push' -l prune -d "Remove remote branches that don't have a local counterpart" complete -f -c git -n '__fish_git_using_command push' -l mirror -d 'Push all refs under refs/' @@ -418,7 +434,7 @@ complete -f -c git -n '__fish_git_using_command push' -l progress -d 'Force prog ### rebase complete -f -c git -n '__fish_git_needs_command' -a rebase -d 'Forward-port local commits to the updated upstream head' -complete -f -c git -n '__fish_git_using_command rebase' -a '(git remote)' -d 'Remote alias' +complete -f -c git -n '__fish_git_using_command rebase' -a '(__fish_git_remotes)' -d 'Remote alias' complete -f -c git -n '__fish_git_using_command rebase' -a '(__fish_git_branches)' -d 'Branch' complete -f -c git -n '__fish_git_using_command rebase' -l continue -d 'Restart the rebasing process' complete -f -c git -n '__fish_git_using_command rebase' -l abort -d 'Abort the rebase operation' From 404ab5e9ab2ba2f0eb9df797993524348b95d586 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 30 Apr 2016 16:30:02 +0200 Subject: [PATCH 178/363] git completion: Allow optional "+" for push This signifies a force-push. To avoid cluttering, only complete branches if a + is already given. Fixes #2879. (cherry picked from commit fde26d404956f843860f2bf5bb00dd3e1636632a) --- share/completions/git.fish | 3 +++ 1 file changed, 3 insertions(+) diff --git a/share/completions/git.fish b/share/completions/git.fish index a6bce22d0..e4e0ba36b 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -417,7 +417,10 @@ complete -f -c git -n '__fish_git_using_command pull; and __fish_git_branch_for_ ### push complete -f -c git -n '__fish_git_needs_command' -a push -d 'Update remote refs along with associated objects' complete -f -c git -n '__fish_git_using_command push; and not __fish_git_branch_for_remote' -a '(__fish_git_remotes)' -d 'Remote alias' +# The "refspec" here is an optional "+" to signify a force-push (implemented) +# then src:dest (where both src and dest are git objects, so we most likely want to complete branches (not implemented) complete -f -c git -n '__fish_git_using_command push; and __fish_git_branch_for_remote' -a '(__fish_git_branches)' -d 'Branch' +complete -f -c git -n '__fish_git_using_command push; and __fish_git_branch_for_remote; and string match -q "+*" -- (commandline -ct)' -a '+(__fish_git_branches)' -d 'Force-push branch' complete -f -c git -n '__fish_git_using_command push' -l all -d 'Push all refs under refs/heads/' complete -f -c git -n '__fish_git_using_command push' -l prune -d "Remove remote branches that don't have a local counterpart" complete -f -c git -n '__fish_git_using_command push' -l mirror -d 'Push all refs under refs/' From 05a432da32ad6c5ff941d7f9a936d53f36c68925 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 30 Apr 2016 16:51:49 +0200 Subject: [PATCH 179/363] Remove using_command from netctl completions This allows `; and netctl` to work. First step towards #2705. --- share/completions/netctl.fish | 61 +++++++++-------------------------- 1 file changed, 16 insertions(+), 45 deletions(-) diff --git a/share/completions/netctl.fish b/share/completions/netctl.fish index ac5c07b57..46d9d8435 100644 --- a/share/completions/netctl.fish +++ b/share/completions/netctl.fish @@ -1,20 +1,4 @@ -function __fish_netctl_needs_command - set cmd (commandline -opc) - if [ (count $cmd) -eq 1 ] - return 0 - end - return 1 -end - -function __fish_netctl_using_command - set cmd (commandline -opc) - if [ (count $cmd) -gt 1 ] - if [ $argv[1] = $cmd[2] ] - return 0 - end - end - return 1 -end +set -l cmds list store restore stop-all start stop restart switch-to status enable disable reenable is-enabled edit function __fish_netctl_get_profiles command netctl list | sed -e 's/^[ \t*]*//' @@ -22,31 +6,18 @@ end complete -f -c netctl -l help -d 'Display help' complete -f -c netctl -l version -d 'Display version' -complete -f -c netctl -n '__fish_netctl_needs_command' -a list -d 'List available profiles' -complete -f -c netctl -n '__fish_netctl_needs_command' -a store -d 'Save which profiles are active' -complete -f -c netctl -n '__fish_netctl_needs_command' -a restore -d 'Load saved profiles' -complete -f -c netctl -n '__fish_netctl_needs_command' -a stop-all -d 'Stops all profiles' - -complete -f -c netctl -n '__fish_netctl_needs_command' -a start -d 'Start a profile' -complete -f -c netctl -n '__fish_netctl_using_command start' -a '(__fish_netctl_get_profiles)' -d 'Profile' - -complete -f -c netctl -n '__fish_netctl_needs_command' -a stop -d 'Stop a profile' -complete -f -c netctl -n '__fish_netctl_using_command stop' -a '(__fish_netctl_get_profiles)' -d 'Profile' - -complete -f -c netctl -n '__fish_netctl_needs_command' -a restart -d 'Restart a profile' -complete -f -c netctl -n '__fish_netctl_using_command restart' -a '(__fish_netctl_get_profiles)' -d 'Profile' - -complete -f -c netctl -n '__fish_netctl_needs_command' -a switch-to -d 'Switch to a profile' -complete -f -c netctl -n '__fish_netctl_using_command switch-to' -a '(__fish_netctl_get_profiles)' -d 'Profile' - -complete -f -c netctl -n '__fish_netctl_needs_command' -a status -d 'Show runtime status of a profile' -complete -f -c netctl -n '__fish_netctl_using_command status' -a '(__fish_netctl_get_profiles)' -d 'Profile' - -complete -f -c netctl -n '__fish_netctl_needs_command' -a enable -d 'Enable the systemd unit for a profile' -complete -f -c netctl -n '__fish_netctl_using_command enable' -a '(__fish_netctl_get_profiles)' -d 'Profile' - -complete -f -c netctl -n '__fish_netctl_needs_command' -a disable -d 'Disable the systemd unit for a profile' -complete -f -c netctl -n '__fish_netctl_using_command disable' -a '(__fish_netctl_get_profiles)' -d 'Profile' - -complete -f -c netctl -n '__fish_netctl_needs_command' -a reenable -d 'Reenable the systemd unit for a profile' -complete -f -c netctl -n '__fish_netctl_using_command reenable' -a '(__fish_netctl_get_profiles)' -d 'Profile' +complete -f -c netctl -n "not __fish_seen_subcommand_from $cmds" -a list -d 'List available profiles' +complete -f -c netctl -n "not __fish_seen_subcommand_from $cmds" -a store -d 'Save which profiles are active' +complete -f -c netctl -n "not __fish_seen_subcommand_from $cmds" -a restore -d 'Load saved profiles' +complete -f -c netctl -n "not __fish_seen_subcommand_from $cmds" -a stop-all -d 'Stops all profiles' +complete -f -c netctl -n "not __fish_seen_subcommand_from $cmds" -a start -d 'Start a profile' +complete -f -c netctl -n "not __fish_seen_subcommand_from $cmds" -a stop -d 'Stop a profile' +complete -f -c netctl -n "not __fish_seen_subcommand_from $cmds" -a restart -d 'Restart a profile' +complete -f -c netctl -n "not __fish_seen_subcommand_from $cmds" -a switch-to -d 'Switch to a profile' +complete -f -c netctl -n "not __fish_seen_subcommand_from $cmds" -a status -d 'Show runtime status of a profile' +complete -f -c netctl -n "not __fish_seen_subcommand_from $cmds" -a enable -d 'Enable the systemd unit for a profile' +complete -f -c netctl -n "not __fish_seen_subcommand_from $cmds" -a disable -d 'Disable the systemd unit for a profile' +complete -f -c netctl -n "not __fish_seen_subcommand_from $cmds" -a reenable -d 'Reenable the systemd unit for a profile' +complete -f -c netctl -n "not __fish_seen_subcommand_from $cmds" -a is-enabled -d 'Check whether the unit is enabled' +complete -f -c netctl -n "not __fish_seen_subcommand_from $cmds" -a edit -d 'Open the specified profile in an editor' +complete -f -c netctl -n "__fish_seen_subcommand_from start stop restart switch-to status enable disable reenable is-enabled edit" -a '(__fish_netctl_get_profiles)' -d 'Profile' From fb1443a8853f2c2d0cbe3b5b0f33c76e704993c4 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 30 Apr 2016 16:51:49 +0200 Subject: [PATCH 180/363] Remove using_command from netctl completions This allows `; and netctl` to work. First step towards #2705. --- share/completions/netctl.fish | 61 +++++++++-------------------------- 1 file changed, 16 insertions(+), 45 deletions(-) diff --git a/share/completions/netctl.fish b/share/completions/netctl.fish index ac5c07b57..46d9d8435 100644 --- a/share/completions/netctl.fish +++ b/share/completions/netctl.fish @@ -1,20 +1,4 @@ -function __fish_netctl_needs_command - set cmd (commandline -opc) - if [ (count $cmd) -eq 1 ] - return 0 - end - return 1 -end - -function __fish_netctl_using_command - set cmd (commandline -opc) - if [ (count $cmd) -gt 1 ] - if [ $argv[1] = $cmd[2] ] - return 0 - end - end - return 1 -end +set -l cmds list store restore stop-all start stop restart switch-to status enable disable reenable is-enabled edit function __fish_netctl_get_profiles command netctl list | sed -e 's/^[ \t*]*//' @@ -22,31 +6,18 @@ end complete -f -c netctl -l help -d 'Display help' complete -f -c netctl -l version -d 'Display version' -complete -f -c netctl -n '__fish_netctl_needs_command' -a list -d 'List available profiles' -complete -f -c netctl -n '__fish_netctl_needs_command' -a store -d 'Save which profiles are active' -complete -f -c netctl -n '__fish_netctl_needs_command' -a restore -d 'Load saved profiles' -complete -f -c netctl -n '__fish_netctl_needs_command' -a stop-all -d 'Stops all profiles' - -complete -f -c netctl -n '__fish_netctl_needs_command' -a start -d 'Start a profile' -complete -f -c netctl -n '__fish_netctl_using_command start' -a '(__fish_netctl_get_profiles)' -d 'Profile' - -complete -f -c netctl -n '__fish_netctl_needs_command' -a stop -d 'Stop a profile' -complete -f -c netctl -n '__fish_netctl_using_command stop' -a '(__fish_netctl_get_profiles)' -d 'Profile' - -complete -f -c netctl -n '__fish_netctl_needs_command' -a restart -d 'Restart a profile' -complete -f -c netctl -n '__fish_netctl_using_command restart' -a '(__fish_netctl_get_profiles)' -d 'Profile' - -complete -f -c netctl -n '__fish_netctl_needs_command' -a switch-to -d 'Switch to a profile' -complete -f -c netctl -n '__fish_netctl_using_command switch-to' -a '(__fish_netctl_get_profiles)' -d 'Profile' - -complete -f -c netctl -n '__fish_netctl_needs_command' -a status -d 'Show runtime status of a profile' -complete -f -c netctl -n '__fish_netctl_using_command status' -a '(__fish_netctl_get_profiles)' -d 'Profile' - -complete -f -c netctl -n '__fish_netctl_needs_command' -a enable -d 'Enable the systemd unit for a profile' -complete -f -c netctl -n '__fish_netctl_using_command enable' -a '(__fish_netctl_get_profiles)' -d 'Profile' - -complete -f -c netctl -n '__fish_netctl_needs_command' -a disable -d 'Disable the systemd unit for a profile' -complete -f -c netctl -n '__fish_netctl_using_command disable' -a '(__fish_netctl_get_profiles)' -d 'Profile' - -complete -f -c netctl -n '__fish_netctl_needs_command' -a reenable -d 'Reenable the systemd unit for a profile' -complete -f -c netctl -n '__fish_netctl_using_command reenable' -a '(__fish_netctl_get_profiles)' -d 'Profile' +complete -f -c netctl -n "not __fish_seen_subcommand_from $cmds" -a list -d 'List available profiles' +complete -f -c netctl -n "not __fish_seen_subcommand_from $cmds" -a store -d 'Save which profiles are active' +complete -f -c netctl -n "not __fish_seen_subcommand_from $cmds" -a restore -d 'Load saved profiles' +complete -f -c netctl -n "not __fish_seen_subcommand_from $cmds" -a stop-all -d 'Stops all profiles' +complete -f -c netctl -n "not __fish_seen_subcommand_from $cmds" -a start -d 'Start a profile' +complete -f -c netctl -n "not __fish_seen_subcommand_from $cmds" -a stop -d 'Stop a profile' +complete -f -c netctl -n "not __fish_seen_subcommand_from $cmds" -a restart -d 'Restart a profile' +complete -f -c netctl -n "not __fish_seen_subcommand_from $cmds" -a switch-to -d 'Switch to a profile' +complete -f -c netctl -n "not __fish_seen_subcommand_from $cmds" -a status -d 'Show runtime status of a profile' +complete -f -c netctl -n "not __fish_seen_subcommand_from $cmds" -a enable -d 'Enable the systemd unit for a profile' +complete -f -c netctl -n "not __fish_seen_subcommand_from $cmds" -a disable -d 'Disable the systemd unit for a profile' +complete -f -c netctl -n "not __fish_seen_subcommand_from $cmds" -a reenable -d 'Reenable the systemd unit for a profile' +complete -f -c netctl -n "not __fish_seen_subcommand_from $cmds" -a is-enabled -d 'Check whether the unit is enabled' +complete -f -c netctl -n "not __fish_seen_subcommand_from $cmds" -a edit -d 'Open the specified profile in an editor' +complete -f -c netctl -n "__fish_seen_subcommand_from start stop restart switch-to status enable disable reenable is-enabled edit" -a '(__fish_netctl_get_profiles)' -d 'Profile' From a897ef002521a3991adc4342189f0e30a0d048b0 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Fri, 29 Apr 2016 15:34:10 -0700 Subject: [PATCH 181/363] don't use colors when writing the ^C indicator There was an extended discussion in https://github.com/fish-shell/fish-shell/issues/2904 about using a bright yellow background to make the cancelled command indicator, ^C, standout. The upshot was that standout (i.e., reversing fg/bg colors) mode should be used until themes are agumented with proper support for background colors and special characters. --- share/functions/__fish_cancel_commandline.fish | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/share/functions/__fish_cancel_commandline.fish b/share/functions/__fish_cancel_commandline.fish index 959e6800b..fb378f250 100644 --- a/share/functions/__fish_cancel_commandline.fish +++ b/share/functions/__fish_cancel_commandline.fish @@ -3,8 +3,13 @@ function __fish_cancel_commandline set -l cmd (commandline) if test -n "$cmd" commandline -C 1000000 - # set a color, output ^C, restore color, clear to EOL (to erase any autosuggestion) - echo -n (set_color -b bryellow black)"^C"(set_color normal)\033"[0K" + # TODO: Switch from using tput and standout mode to `set_color` when themes have been + # augmented to include support for background colors or has support for standout/reverse + # mode. + # + # Set reverse fg/bg color mode, output ^C, restore normal mode, clear to EOL (to erase any + # autosuggestion). + echo (tput smso)"^C"(tput rmso)(tput el) for i in (seq (commandline -L)) echo "" end From 1cd4731c2dae4bddd06e30d96d9ae9a78ed65ef9 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Fri, 29 Apr 2016 15:34:10 -0700 Subject: [PATCH 182/363] don't use colors when writing the ^C indicator There was an extended discussion in https://github.com/fish-shell/fish-shell/issues/2904 about using a bright yellow background to make the cancelled command indicator, ^C, standout. The upshot was that standout (i.e., reversing fg/bg colors) mode should be used until themes are agumented with proper support for background colors and special characters. (cherry picked from commit a897ef002521a3991adc4342189f0e30a0d048b0) --- .../functions/__fish_cancel_commandline.fish | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 share/functions/__fish_cancel_commandline.fish diff --git a/share/functions/__fish_cancel_commandline.fish b/share/functions/__fish_cancel_commandline.fish new file mode 100644 index 000000000..fb378f250 --- /dev/null +++ b/share/functions/__fish_cancel_commandline.fish @@ -0,0 +1,19 @@ +# This is meant to be bound to something like \cC. +function __fish_cancel_commandline + set -l cmd (commandline) + if test -n "$cmd" + commandline -C 1000000 + # TODO: Switch from using tput and standout mode to `set_color` when themes have been + # augmented to include support for background colors or has support for standout/reverse + # mode. + # + # Set reverse fg/bg color mode, output ^C, restore normal mode, clear to EOL (to erase any + # autosuggestion). + echo (tput smso)"^C"(tput rmso)(tput el) + for i in (seq (commandline -L)) + echo "" + end + commandline "" + commandline -f repaint + end +end From 58d7c4b388067771e0ce1cf0dffd376c6b7fb9f2 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sat, 30 Apr 2016 17:46:14 -0700 Subject: [PATCH 183/363] Remove use of __environ It has apparently never worked. Fixes #2988 --- configure.ac | 24 ------------------------ osx/config.h | 3 --- src/env.cpp | 4 +--- src/fallback.cpp | 6 ------ 4 files changed, 1 insertion(+), 36 deletions(-) diff --git a/configure.ac b/configure.ac index 87bf46b7c..0236fc8a1 100644 --- a/configure.ac +++ b/configure.ac @@ -675,30 +675,6 @@ else AC_MSG_RESULT(no) fi -# Check for __environ symbol -AC_MSG_CHECKING([for __environ symbol]) -AC_TRY_LINK( - [ - #include - ], - [ - extern char **__environ; - char **tmp = __environ; - exit(tmp!=0); - ], - have___environ=yes, - have___environ=no -) -if test "$have___environ" = yes; then - AC_MSG_RESULT(yes) - AC_DEFINE( - [HAVE___ENVIRON], - [1], - [Define to 1 if the __environ symbol is exported.] - ) -else - AC_MSG_RESULT(no) -fi # Check for sys_errlist AC_MSG_CHECKING([for sys_errlist array]) diff --git a/osx/config.h b/osx/config.h index 0a5530168..fd61d2730 100644 --- a/osx/config.h +++ b/osx/config.h @@ -206,9 +206,6 @@ /* Define to 1 if the _sys_errs array is available. */ /* #undef HAVE__SYS__ERRS */ -/* Define to 1 if the __environ symbol is exported. */ -/* #undef HAVE___ENVIRON */ - /* Define to 1 to disable ncurses macros that conflict with the STL */ #define NCURSES_NOMACROS 1 diff --git a/src/env.cpp b/src/env.cpp index 40af30bfb..e4829a463 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -45,8 +45,6 @@ /// At init, we read all the environment variables from this array. extern char **environ; -/// This should be the same thing as \c environ, but it is possible only one of the two work... -extern char **__environ; bool g_log_forks = false; bool g_use_posix_spawn = false; // will usually be set to true @@ -311,7 +309,7 @@ void env_init(const struct config_paths_t *paths /* or NULL */) { // Import environment variables. Walk backwards so that the first one out of any duplicates wins // (#2784). wcstring key, val; - const char *const *envp = (environ ? environ : __environ); + const char *const *envp = environ; size_t i = 0; while (envp && envp[i]) { i++; diff --git a/src/fallback.cpp b/src/fallback.cpp index e3dcfed0b..b53aec741 100644 --- a/src/fallback.cpp +++ b/src/fallback.cpp @@ -46,12 +46,6 @@ #include "fallback.h" // IWYU pragma: keep #include "util.h" // IWYU pragma: keep -#ifndef HAVE___ENVIRON - -char **__environ = 0; - -#endif - #ifdef TPUTS_KLUDGE int tputs(const char *str, int affcnt, int (*fish_putc)(tputs_arg_t)) From aa8840b423177dfe3efbbee5bdc891ecf9f7d20f Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Fri, 29 Apr 2016 19:01:36 -0700 Subject: [PATCH 184/363] restyle fallback module to match project style Reduces lint errors from 36 to 33 (-8%). Line count from 1910 to 1476 (-23%). Another step in resolving issue #2902. This also fixes a stupid mistake from an earlier commit where I didn't realize that osx/config.h was meant to be included as a semi-static file in the repository. --- Makefile.in | 2 +- src/fallback.cpp | 1177 +++++++++++++++++----------------------------- src/fallback.h | 345 +++++--------- 3 files changed, 546 insertions(+), 978 deletions(-) diff --git a/Makefile.in b/Makefile.in index b19fe6bb8..6172721e8 100644 --- a/Makefile.in +++ b/Makefile.in @@ -850,7 +850,7 @@ style-all: # distclean: clean $(MAKE) -C $(PCRE2_DIR) distclean || true - rm -f config.status config.log config.h Makefile osx/config.h + rm -f config.status config.log config.h Makefile .PHONY: distclean # diff --git a/src/fallback.cpp b/src/fallback.cpp index b53aec741..7dcaaee77 100644 --- a/src/fallback.cpp +++ b/src/fallback.cpp @@ -1,30 +1,26 @@ -/** - This file only contains fallback implementations of functions which - have been found to be missing or broken by the configuration - scripts. - - Many of these functions are more or less broken and - incomplete. lrand28_r internally uses the regular (bad) rand_r - function, the gettext function doesn't actually do anything, etc. -*/ +// This file only contains fallback implementations of functions which have been found to be missing +// or broken by the configuration scripts. +// +// Many of these functions are more or less broken and incomplete. lrand28_r internally uses the +// regular (bad) rand_r function, the gettext function doesn't actually do anything, etc. #include "config.h" // IWYU likes to recommend adding term.h when we want ncurses.h. // IWYU pragma: no_include term.h +#include // IWYU pragma: keep +#include // IWYU pragma: keep +#include // IWYU pragma: keep +#include // IWYU pragma: keep +#include // IWYU pragma: keep +#include // IWYU pragma: keep +#include // IWYU pragma: keep #include -#include // IWYU pragma: keep -#include +#include +#include // IWYU pragma: keep #include // IWYU pragma: keep -#include // IWYU pragma: keep -#include // IWYU pragma: keep -#include // IWYU pragma: keep +#include #include #include -#include -#include // IWYU pragma: keep -#include // IWYU pragma: keep -#include // IWYU pragma: keep -#include // IWYU pragma: keep #if HAVE_GETTEXT #include #endif @@ -41,72 +37,60 @@ #include #endif #include // IWYU pragma: keep -#include // IWYU pragma: keep +#include // IWYU pragma: keep #include "fallback.h" // IWYU pragma: keep -#include "util.h" // IWYU pragma: keep +#include "util.h" // IWYU pragma: keep #ifdef TPUTS_KLUDGE - -int tputs(const char *str, int affcnt, int (*fish_putc)(tputs_arg_t)) -{ - while (*str) - { +int tputs(const char *str, int affcnt, int (*fish_putc)(tputs_arg_t)) { + while (*str) { fish_putc(*str++); } } - #endif #ifdef TPARM_SOLARIS_KLUDGE - #undef tparm -/** - Checks for known string values and maps to correct number of parameters. -*/ -char *tparm_solaris_kludge(char *str, ...) -{ - long int param[9] = { }; +/// Checks for known string values and maps to correct number of parameters. +char *tparm_solaris_kludge(char *str, ...) { + long int param[9] = {}; va_list ap; va_start(ap, str); - if ((set_a_foreground && ! strcmp(str, set_a_foreground)) - || (set_a_background && ! strcmp(str, set_a_background)) - || (set_foreground && ! strcmp(str, set_foreground)) - || (set_background && ! strcmp(str, set_background)) - || (enter_underline_mode && ! strcmp(str, enter_underline_mode)) - || (exit_underline_mode && ! strcmp(str, exit_underline_mode)) - || (enter_standout_mode && ! strcmp(str, enter_standout_mode)) - || (exit_standout_mode && ! strcmp(str, exit_standout_mode)) - || (flash_screen && ! strcmp(str, flash_screen)) - || (enter_subscript_mode && ! strcmp(str, enter_subscript_mode)) - || (exit_subscript_mode && ! strcmp(str, exit_subscript_mode)) - || (enter_superscript_mode && ! strcmp(str, enter_superscript_mode)) - || (exit_superscript_mode && ! strcmp(str, exit_superscript_mode)) - || (enter_blink_mode && ! strcmp(str, enter_blink_mode)) - || (enter_italics_mode && ! strcmp(str, enter_italics_mode)) - || (exit_italics_mode && ! strcmp(str, exit_italics_mode)) - || (enter_reverse_mode && ! strcmp(str, enter_reverse_mode)) - || (enter_shadow_mode && ! strcmp(str, enter_shadow_mode)) - || (exit_shadow_mode && ! strcmp(str, exit_shadow_mode)) - || (enter_secure_mode && ! strcmp(str, enter_secure_mode)) - || (enter_bold_mode && ! strcmp(str, enter_bold_mode))) - { + if ((set_a_foreground && !strcmp(str, set_a_foreground)) || + (set_a_background && !strcmp(str, set_a_background)) || + (set_foreground && !strcmp(str, set_foreground)) || + (set_background && !strcmp(str, set_background)) || + (enter_underline_mode && !strcmp(str, enter_underline_mode)) || + (exit_underline_mode && !strcmp(str, exit_underline_mode)) || + (enter_standout_mode && !strcmp(str, enter_standout_mode)) || + (exit_standout_mode && !strcmp(str, exit_standout_mode)) || + (flash_screen && !strcmp(str, flash_screen)) || + (enter_subscript_mode && !strcmp(str, enter_subscript_mode)) || + (exit_subscript_mode && !strcmp(str, exit_subscript_mode)) || + (enter_superscript_mode && !strcmp(str, enter_superscript_mode)) || + (exit_superscript_mode && !strcmp(str, exit_superscript_mode)) || + (enter_blink_mode && !strcmp(str, enter_blink_mode)) || + (enter_italics_mode && !strcmp(str, enter_italics_mode)) || + (exit_italics_mode && !strcmp(str, exit_italics_mode)) || + (enter_reverse_mode && !strcmp(str, enter_reverse_mode)) || + (enter_shadow_mode && !strcmp(str, enter_shadow_mode)) || + (exit_shadow_mode && !strcmp(str, exit_shadow_mode)) || + (enter_secure_mode && !strcmp(str, enter_secure_mode)) || + (enter_bold_mode && !strcmp(str, enter_bold_mode))) { param[0] = va_arg(ap, long int); - } - else if (cursor_address && ! strcmp(str, cursor_address)) - { + } else if (cursor_address && !strcmp(str, cursor_address)) { param[0] = va_arg(ap, long int); param[1] = va_arg(ap, long int); } va_end(ap); - - return tparm(str, param[0], param[1], param[2], param[3], - param[4], param[5], param[6], param[7], param[8]); + return tparm(str, param[0], param[1], param[2], param[3], param[4], param[5], param[6], + param[7], param[8]); } // Re-defining just to make sure nothing breaks further down in this file. @@ -114,7 +98,6 @@ char *tparm_solaris_kludge(char *str, ...) #endif - #ifndef HAVE_FWPRINTF #define INTERNAL_FWPRINTF 1 #endif @@ -125,236 +108,185 @@ char *tparm_solaris_kludge(char *str, ...) #ifdef INTERNAL_FWPRINTF -/** - Internal function for the wprintf fallbacks. USed to write the - specified number of spaces. -*/ -static void pad(void (*writer)(wchar_t), int count) -{ - +/// Internal function for the wprintf fallbacks. USed to write the specified number of spaces. +static void pad(void (*writer)(wchar_t), int count) { int i; - if (count < 0) - return; + if (count < 0) return; - for (i=0; i= L'0') && (*filter <= L'9')) - { - width=10*width+(*filter++ - L'0'); + if (iswdigit(*filter)) { + width = 0; + while ((*filter >= L'0') && (*filter <= L'9')) { + width = 10 * width + (*filter++ - L'0'); } } - while (loop) - { - - switch (*filter) - { - case L'l': - /* Long variable */ + while (loop) { + switch (*filter) { + case L'l': { + // Long variable. is_long++; filter++; break; - - case L'*': - /* Set minimum field width */ + } + case L'*': { + // Set minimum field width. width = va_arg(va, int); filter++; break; - - case L'-': + } + case L'-': { filter++; - pad_left=0; + pad_left = 0; break; - - case L'.': - /* - Set precision. - */ + } + case L'.': { + // Set precision. filter++; - if (*filter == L'*') - { + if (*filter == L'*') { precision = va_arg(va, int); - } - else - { - precision=0; - while ((*filter >= L'0') && (*filter <= L'9')) - { - precision=10*precision+(*filter++ - L'0'); + } else { + precision = 0; + while ((*filter >= L'0') && (*filter <= L'9')) { + precision = 10 * precision + (*filter++ - L'0'); } } break; - - default: - loop=0; + } + default: { + loop = 0; break; + } } } - switch (*filter) - { - case L'c': - { + switch (*filter) { + case L'c': { wchar_t c; - if ((width >= 0) && pad_left) - { - pad(writer, width-1); - count += maxi(width-1, 0); + 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); + 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); + if ((width >= 0) && !pad_left) { + pad(writer, width - 1); + count += maxi(width - 1, 0); } count++; - break; } - case L's': - { - - wchar_t *ss=0; + case L's': { + wchar_t *ss = 0; wcstring wide_ss; - if (is_long) - { + if (is_long) { ss = va_arg(va, wchar_t *); - } - else - { - char *ns = va_arg(va, char*); + } else { + char *ns = va_arg(va, char *); - if (ns) - { + if (ns) { wide_ss = str2wcstring(ns); ss = wide_ss.c_str(); } } - if (!ss) - { + if (!ss) { return -1; } - if ((width >= 0) && pad_left) - { - pad(writer, width-wcslen(ss)); - count += maxi(width-wcslen(ss), 0); + if ((width >= 0) && pad_left) { + pad(writer, width - wcslen(ss)); + count += maxi(width - wcslen(ss), 0); } - wchar_t *s=ss; + wchar_t *s = ss; int precount = count; - while (*s) - { - if ((precision > 0) && (precision <= (count-precount))) - break; - + 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 ((width >= 0) && !pad_left) { + pad(writer, width - wcslen(ss)); + count += maxi(width - wcslen(ss), 0); } break; } - case L'd': case L'i': case L'o': case L'u': case L'x': - case L'X': - { + case L'X': { char str[33]; char *pos; char format[16]; int len; - format[0]=0; + format[0] = 0; strcat(format, "%"); - if (precision >= 0) - strcat(format, ".*"); - switch (is_long) - { - case 2: + if (precision >= 0) strcat(format, ".*"); + switch (is_long) { + case 2: { strcat(format, "ll"); break; - case 1: + } + case 1: { strcat(format, "l"); break; + } } len = strlen(format); - format[len++]=(char)*filter; - format[len]=0; + format[len++] = (char)*filter; + format[len] = 0; - switch (*filter) - { + switch (*filter) { case L'd': - case L'i': - { - - switch (is_long) - { - case 0: - { + case L'i': { + switch (is_long) { + case 0: { int d = va_arg(va, int); if (precision >= 0) snprintf(str, 32, format, precision, d); @@ -363,9 +295,7 @@ static int vgwprintf(void (*writer)(wchar_t), break; } - - case 1: - { + case 1: { long d = va_arg(va, long); if (precision >= 0) snprintf(str, 32, format, precision, d); @@ -373,9 +303,7 @@ static int vgwprintf(void (*writer)(wchar_t), snprintf(str, 32, format, d); break; } - - case 2: - { + case 2: { long long d = va_arg(va, long long); if (precision >= 0) snprintf(str, 32, format, precision, d); @@ -383,25 +311,21 @@ static int vgwprintf(void (*writer)(wchar_t), snprintf(str, 32, format, d); break; } - - default: - debug(0, L"Invalid length modifier in string %ls\n", filter_org); + 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: - { + case L'X': { + switch (is_long) { + case 0: { unsigned d = va_arg(va, unsigned); if (precision >= 0) snprintf(str, 32, format, precision, d); @@ -409,9 +333,7 @@ static int vgwprintf(void (*writer)(wchar_t), snprintf(str, 32, format, d); break; } - - case 1: - { + case 1: { unsigned long d = va_arg(va, unsigned long); if (precision >= 0) snprintf(str, 32, format, precision, d); @@ -419,9 +341,7 @@ static int vgwprintf(void (*writer)(wchar_t), snprintf(str, 32, format, d); break; } - - case 2: - { + case 2: { unsigned long long d = va_arg(va, unsigned long long); if (precision >= 0) snprintf(str, 32, format, precision, d); @@ -429,39 +349,35 @@ static int vgwprintf(void (*writer)(wchar_t), snprintf(str, 32, format, d); break; } - - default: - debug(0, L"Invalid length modifier in string %ls\n", filter_org); + default: { + debug(0, L"Invalid length modifier in string %ls\n", + filter_org); return -1; + } } break; - } - - default: + 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); + if ((width >= 0) && pad_left) { + int l = maxi(width - strlen(str), 0); pad(writer, l); count += l; } pos = str; - while (*pos) - { + while (*pos) { writer(*(pos++)); count++; } - if ((width >= 0) && !pad_left) - { - int l = maxi(width-strlen(str), 0); + if ((width >= 0) && !pad_left) { + int l = maxi(width - strlen(str), 0); pad(writer, l); count += l; } @@ -469,39 +385,28 @@ static int vgwprintf(void (*writer)(wchar_t), break; } - case L'f': - { + case L'f': { char str[32]; char *pos; double val = va_arg(va, double); - if (precision>= 0) - { - if (width>= 0) - { + if (precision >= 0) { + if (width >= 0) { snprintf(str, 32, "%*.*f", width, precision, val); - } - else - { + } else { snprintf(str, 32, "%.*f", precision, val); } - } - else - { - if (width>= 0) - { + } else { + if (width >= 0) { snprintf(str, 32, "%*f", width, val); - } - else - { + } else { snprintf(str, 32, "%f", val); } } pos = str; - while (*pos) - { + while (*pos) { writer(*(pos++)); count++; } @@ -509,26 +414,23 @@ static int vgwprintf(void (*writer)(wchar_t), break; } - case L'n': - { + case L'n': { int *n = va_arg(va, int *); *n = count; break; } - case L'%': - { + case L'%': { writer('%'); count++; break; } - default: + default: { debug(0, L"Unknown switch %lc in string %ls\n", *filter, filter_org); return -1; + } } - } - else - { + } else { writer(*filter); count++; } @@ -537,51 +439,36 @@ static int vgwprintf(void (*writer)(wchar_t), return count; } -/** - Holds data for swprintf writer -*/ -static struct -{ +/// Holds data for swprintf writer. +static struct { int count; int max; wchar_t *pos; -} -sw_data; +} sw_data; -/** - Writers for string output -*/ -static void sw_writer(wchar_t c) -{ - if (sw_data.count < sw_data.max) - *(sw_data.pos++)=c; +/// Writers for string output. +static void sw_writer(wchar_t c) { + 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; - sw_data.pos=out; - sw_data.max=n; - sw_data.count=0; - written=vgwprintf(&sw_writer, - filter, - va); - if (written < n) - { + 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; + } else { + written = -1; } 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; @@ -591,27 +478,18 @@ int swprintf(wchar_t *out, size_t n, const wchar_t *filter, ...) return written; } -/** - Holds auxiliary data for fwprintf and wprintf writer -*/ +/// Holds auxiliary data for fwprintf and wprintf writer. static FILE *fw_data; -static void fw_writer(wchar_t c) -{ - fputwc(c, fw_data); -} +static void fw_writer(wchar_t c) { fputwc(c, fw_data); } -/* - Writers for file output -*/ -int vfwprintf(FILE *f, const wchar_t *filter, va_list va) -{ +/// Writers for file output. +int vfwprintf(FILE *f, const wchar_t *filter, va_list 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; @@ -621,18 +499,14 @@ int fwprintf(FILE *f, const wchar_t *filter, ...) return written; } -int vwprintf(const wchar_t *filter, va_list va) -{ - return vfwprintf(stdout, filter, va); -} +int vwprintf(const wchar_t *filter, va_list va) { return vfwprintf(stdout, filter, va); } -int wprintf(const wchar_t *filter, ...) -{ +int wprintf(const wchar_t *filter, ...) { va_list va; int written; va_start(va, filter); - written=vwprintf(filter, va); + written = vwprintf(filter, va); va_end(va); return written; } @@ -640,66 +514,58 @@ int wprintf(const wchar_t *filter, ...) #endif #ifndef HAVE_FGETWC -wint_t fgetwc(FILE *stream) -{ +wint_t fgetwc(FILE *stream) { wchar_t res; mbstate_t state = {}; - while (1) - { + while (1) { int b = fgetc(stream); - if (b == EOF) - { + if (b == EOF) { return WEOF; } - if (MB_CUR_MAX == 1) // single-byte locale, all values are legal + if (MB_CUR_MAX == 1) // single-byte locale, all values are legal { return b; } char bb = b; size_t sz = mbrtowc(&res, &bb, 1, &state); - switch (sz) - { - case -1: + switch (sz) { + case -1: { return WEOF; - case -2: + } + case -2: { break; - case 0: + } + case 0: { return 0; - default: - return res; + } + default: { return res; } } } } #endif #ifndef HAVE_FPUTWC -wint_t fputwc(wchar_t wc, FILE *stream) -{ +wint_t fputwc(wchar_t wc, FILE *stream) { int res = 0; mbstate_t state = {}; char s[MB_CUR_MAX + 1] = {}; - if (MB_CUR_MAX == 1) // single-byte locale (C/POSIX/ISO-8859) + if (MB_CUR_MAX == 1) // single-byte locale (C/POSIX/ISO-8859) { // If `wc` contains a wide character we emit a question-mark. - if (wc & ~0xFF) - { + if (wc & ~0xFF) { wc = '?'; } s[0] = (char)wc; res = fputs(s, stream); - } - else - { + } else { size_t len = wcrtomb(s, wc, &state); - if (len == (size_t)-1) - { + if (len == (size_t)-1) { debug(1, L"Wide character %d has no narrow representation", wc); - } - else { + } else { res = fputs(s, stream); } } @@ -710,21 +576,15 @@ wint_t fputwc(wchar_t wc, FILE *stream) #ifndef HAVE_WCSTOK -/* - Used by fallback wcstok. Borrowed from glibc -*/ -static size_t fish_wcsspn(const wchar_t *wcs, - const wchar_t *accept) -{ +/// Used by fallback wcstok. Borrowed from glibc. +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; - for (p = wcs; *p != L'\0'; ++p) - { + for (p = wcs; *p != L'\0'; ++p) { for (a = accept; *a != L'\0'; ++a) - if (*p == *a) - break; + if (*p == *a) break; if (*a == L'\0') return count; @@ -734,59 +594,46 @@ static size_t fish_wcsspn(const wchar_t *wcs, return count; } -/* - Used by fallback wcstok. Borrowed from glibc -*/ -static wchar_t *fish_wcspbrk(const wchar_t *wcs, const wchar_t *accept) -{ +/// Used by fallback wcstok. Borrowed from glibc. +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 (wchar_t *)wcs; return NULL; } -/* - Fallback wcstok implementation. Borrowed from glibc. -*/ -wchar_t *wcstok(wchar_t *wcs, const wchar_t *delim, wchar_t **save_ptr) -{ +/// Fallback wcstok implementation. Borrowed from glibc. +wchar_t *wcstok(wchar_t *wcs, const wchar_t *delim, wchar_t **save_ptr) { wchar_t *result; - if (wcs == NULL) - { - if (*save_ptr == NULL) - { + if (wcs == NULL) { + if (*save_ptr == NULL) { errno = EINVAL; return NULL; - } - else + } else wcs = *save_ptr; } - /* Scan leading delimiters. */ + // Scan leading delimiters. wcs += fish_wcsspn(wcs, delim); - if (*wcs == L'\0') - { + if (*wcs == L'\0') { *save_ptr = NULL; return NULL; } - /* Find the end of the token. */ + // Find the end of the token. result = wcs; wcs = fish_wcspbrk(result, delim); - if (wcs == NULL) - { - /* This token finishes the string. */ + if (wcs == NULL) { + // This token finishes the string. *save_ptr = NULL; - } - else - { - /* Terminate the token and make *SAVE_PTR point past it. */ + } else { + // Terminate the token and make *SAVE_PTR point past it. *wcs = L'\0'; *save_ptr = wcs + 1; } @@ -795,162 +642,120 @@ wchar_t *wcstok(wchar_t *wcs, const wchar_t *delim, wchar_t **save_ptr) #endif -/* Fallback implementations of wcsdup and wcscasecmp. On systems where these are not needed (e.g. building on Linux) these should end up just being stripped, as they are static functions that are not referenced in this file. -*/ -__attribute__((unused)) -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) - { +/// Fallback implementations of wcsdup and wcscasecmp. On systems where these are not needed (e.g. +/// building on Linux) these should end up just being stripped, as they are static functions that +/// are not referenced in this file. +__attribute__((unused)) 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; } - memcpy(out, in, sizeof(wchar_t)*(len+1)); + memcpy(out, in, sizeof(wchar_t) * (len + 1)); return out; } -__attribute__((unused)) -static int wcscasecmp_fallback(const wchar_t *a, const wchar_t *b) -{ - if (*a == 0) - { - return (*b==0)?0:-1; - } - else if (*b == 0) - { +__attribute__((unused)) static 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); + int diff = towlower(*a) - towlower(*b); if (diff != 0) return diff; else - return wcscasecmp_fallback(a+1,b+1); + return wcscasecmp_fallback(a + 1, b + 1); } -__attribute__((unused)) -static int wcsncasecmp_fallback(const wchar_t *a, const wchar_t *b, size_t count) -{ - if (count == 0) - return 0; +__attribute__((unused)) static int wcsncasecmp_fallback(const wchar_t *a, const wchar_t *b, + size_t count) { + if (count == 0) return 0; - if (*a == 0) - { - return (*b==0)?0:-1; - } - else if (*b == 0) - { + if (*a == 0) { + return (*b == 0) ? 0 : -1; + } else if (*b == 0) { return 1; } - int diff = towlower(*a)-towlower(*b); + int diff = towlower(*a) - towlower(*b); if (diff != 0) return diff; else - return wcsncasecmp_fallback(a+1,b+1, count-1); + return wcsncasecmp_fallback(a + 1, b + 1, count - 1); } - #if __APPLE__ && __DARWIN_C_LEVEL >= 200809L -/* Note parens avoid the macro expansion */ -wchar_t *wcsdup_use_weak(const wchar_t *a) -{ - if (&wcsdup != NULL) - return (wcsdup)(a); +// Note parens avoid the macro expansion. +wchar_t *wcsdup_use_weak(const wchar_t *a) { + if (&wcsdup != NULL) return (wcsdup)(a); return wcsdup_fallback(a); } -int wcscasecmp_use_weak(const wchar_t *a, const wchar_t *b) -{ - if (&wcscasecmp != NULL) - return (wcscasecmp)(a, b); +int wcscasecmp_use_weak(const wchar_t *a, const wchar_t *b) { + if (&wcscasecmp != NULL) return (wcscasecmp)(a, b); return wcscasecmp_fallback(a, b); } -int wcsncasecmp_use_weak(const wchar_t *s1, const wchar_t *s2, size_t n) -{ - if (&wcsncasecmp != NULL) - return (wcsncasecmp)(s1, s2, n); +int wcsncasecmp_use_weak(const wchar_t *s1, const wchar_t *s2, size_t n) { + if (&wcsncasecmp != NULL) return (wcsncasecmp)(s1, s2, n); return wcsncasecmp_fallback(s1, s2, n); } -#else //__APPLE__ +#else //__APPLE__ #ifndef HAVE_WCSDUP -wchar_t *wcsdup(const wchar_t *in) -{ - return wcsdup_fallback(in); - -} +wchar_t *wcsdup(const wchar_t *in) { return wcsdup_fallback(in); } #endif - #ifndef HAVE_WCSCASECMP -int wcscasecmp(const wchar_t *a, const wchar_t *b) -{ - return wcscasecmp_fallback(a, b); -} +int wcscasecmp(const wchar_t *a, const wchar_t *b) { return wcscasecmp_fallback(a, b); } #endif -#endif //__APPLE__ +#endif //__APPLE__ #ifndef HAVE_WCSLEN -size_t wcslen(const wchar_t *in) -{ - const wchar_t *end=in; - while (*end) - end++; - return end-in; +size_t wcslen(const wchar_t *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, size_t count) -{ +int wcsncasecmp(const wchar_t *a, const wchar_t *b, size_t count) { return wcsncasecmp_fallback(a, b, count); } #endif #ifndef HAVE_WCWIDTH -int wcwidth(wchar_t c) -{ - if (c < 32) - return 0; - if (c == 127) - return 0; +int wcwidth(wchar_t c) { + if (c < 32) return 0; + if (c == 127) return 0; return 1; } #endif #ifndef HAVE_WCSNDUP -wchar_t *wcsndup(const wchar_t *in, size_t c) -{ - wchar_t *res = (wchar_t *)malloc(sizeof(wchar_t)*(c+1)); - if (res == 0) - { +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); + wcslcpy(res, in, c + 1); return res; } #endif -long convert_digit(wchar_t d, int base) -{ - long res=-1; - if ((d <= L'9') && (d >= L'0')) - { +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')) - { + } else if ((d <= L'z') && (d >= L'a')) { res = d + 10 - L'a'; - } - else if ((d <= L'Z') && (d >= L'A')) - { + } else if ((d <= L'Z') && (d >= L'A')) { res = d + 10 - L'A'; } - if (res >= base) - { + if (res >= base) { res = -1; } @@ -958,40 +763,30 @@ long convert_digit(wchar_t d, int base) } #ifndef HAVE_WCSTOL -long wcstol(const wchar_t *nptr, - wchar_t **endptr, - int base) -{ - long long res=0; - int is_set=0; - if (base > 36) - { +long wcstol(const wchar_t *nptr, wchar_t **endptr, int base) { + long long res = 0; + int is_set = 0; + if (base > 36) { errno = EINVAL; return 0; } - while (1) - { + while (1) { long nxt = convert_digit(*nptr, base); - if (endptr != 0) - *endptr = (wchar_t *)nptr; - if (nxt < 0) - { - if (!is_set) - { + if (endptr != 0) *endptr = (wchar_t *)nptr; + if (nxt < 0) { + if (!is_set) { errno = EINVAL; } return res; } - res = (res*base)+nxt; + res = (res * base) + nxt; is_set = 1; - if (res > LONG_MAX) - { + if (res > LONG_MAX) { errno = ERANGE; return LONG_MAX; } - if (res < LONG_MIN) - { + if (res < LONG_MIN) { errno = ERANGE; return LONG_MIN; } @@ -1020,29 +815,22 @@ long wcstol(const wchar_t *nptr, * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -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) { 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; - if (n == 0) - return(dlen + wcslen(s)); + if (n == 0) return (dlen + wcslen(s)); - while (*s != '\0') - { - if (n != 1) - { + while (*s != '\0') { + if (n != 1) { *d++ = *s; n--; } @@ -1050,7 +838,7 @@ wcslcat(wchar_t *dst, const wchar_t *src, size_t siz) } *d = '\0'; - return(dlen + (s - src)); + return (dlen + (s - src)); /* count does not include NUL */ } @@ -1075,49 +863,39 @@ wcslcat(wchar_t *dst, const wchar_t *src, size_t siz) * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -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) { register wchar_t *d = dst; register const wchar_t *s = src; register size_t n = siz; - /* Copy as many bytes as will fit */ - if (n != 0 && --n != 0) - { - do - { - if ((*d++ = *s++) == 0) - break; - } - while (--n != 0); + // Copy as many bytes as will fit. + if (n != 0 && --n != 0) { + do { + if ((*d++ = *s++) == 0) break; + } while (--n != 0); } - /* Not enough room in dst, add NUL and traverse rest of src */ - if (n == 0) - { - if (siz != 0) - *d = '\0'; - /* NUL-terminate dst */ + // 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 */ + return (s - src - 1); + // Count does not include NUL. } #endif #ifndef HAVE_LRAND48_R -int lrand48_r(struct drand48_data *buffer, long int *result) -{ +int lrand48_r(struct drand48_data *buffer, long int *result) { *result = rand_r(&buffer->seed); return 0; } -int srand48_r(long int seedval, struct drand48_data *buffer) -{ +int srand48_r(long int seedval, struct drand48_data *buffer) { buffer->seed = (unsigned int)seedval; return 0; } @@ -1126,8 +904,7 @@ int srand48_r(long int seedval, struct drand48_data *buffer) #ifndef HAVE_FUTIMES -int futimes(int fd, const struct timeval *times) -{ +int futimes(int fd, const struct timeval *times) { errno = ENOSYS; return -1; } @@ -1136,139 +913,98 @@ int futimes(int fd, const struct timeval *times) #if HAVE_GETTEXT -char * fish_gettext(const char * msgid) -{ - return gettext(msgid);; +char *fish_gettext(const char *msgid) { + return gettext(msgid); + ; } -char * fish_bindtextdomain(const char * domainname, const char * dirname) -{ +char *fish_bindtextdomain(const char *domainname, const char *dirname) { return bindtextdomain(domainname, dirname); } -char * fish_textdomain(const char * domainname) -{ - return textdomain(domainname); -} +char *fish_textdomain(const char *domainname) { return textdomain(domainname); } #else -char *fish_gettext(const char * msgid) -{ - return (char *)msgid; -} +char *fish_gettext(const char *msgid) { return (char *)msgid; } -char *fish_bindtextdomain(const char * domainname, const char * dirname) -{ - return NULL; -} +char *fish_bindtextdomain(const char *domainname, const char *dirname) { return NULL; } -char *fish_textdomain(const char * domainname) -{ - return NULL; -} +char *fish_textdomain(const char *domainname) { return NULL; } #endif #if HAVE_DCGETTEXT -char *fish_dcgettext(const char * domainname, const char * msgid, int category) -{ +char *fish_dcgettext(const char *domainname, const char *msgid, int category) { return dcgettext(domainname, msgid, category); } #else -char *fish_dcgettext(const char * domainname, const char * msgid, int category) -{ +char *fish_dcgettext(const char *domainname, const char *msgid, int category) { return (char *)msgid; } - #endif #ifndef HAVE__NL_MSG_CAT_CNTR -int _nl_msg_cat_cntr=0; +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); } #endif #ifndef HAVE_BACKTRACE -int backtrace(void **buffer, int size) -{ - return 0; -} +int backtrace(void **buffer, int size) { return 0; } #endif #ifndef HAVE_BACKTRACE_SYMBOLS_FD -char ** backtrace_symbols_fd(void *const *buffer, int size, int fd) -{ - return 0; -} +char **backtrace_symbols_fd(void *const *buffer, int size, int fd) { return 0; } #endif #ifndef HAVE_SYSCONF -long sysconf(int name) -{ - if (name == _SC_ARG_MAX) - { +long sysconf(int name) { + if (name == _SC_ARG_MAX) { #ifdef ARG_MAX return ARG_MAX; #endif } return -1; - } #endif #ifndef HAVE_NAN -double nan(char *tagp) -{ - return 0.0/0.0; -} +double nan(char *tagp) { 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 */ +// Big hack to use our versions of wcswidth where we know them to be broken, like on OS X. #ifndef HAVE_BROKEN_WCWIDTH #define HAVE_BROKEN_WCWIDTH 1 #endif -#if ! HAVE_BROKEN_WCWIDTH +#if !HAVE_BROKEN_WCWIDTH -int fish_wcwidth(wchar_t wc) -{ - return wcwidth(wc); -} +int fish_wcwidth(wchar_t wc) { return wcwidth(wc); } -int fish_wcswidth(const wchar_t *str, size_t n) -{ - return wcswidth(str, n); -} +int fish_wcswidth(const wchar_t *str, size_t n) { return wcswidth(str, n); } #else static int mk_wcwidth(wchar_t wc); static int mk_wcswidth(const wchar_t *pwcs, size_t n); -int fish_wcwidth(wchar_t wc) -{ - return mk_wcwidth(wc); -} +int fish_wcwidth(wchar_t wc) { return mk_wcwidth(wc); } -int fish_wcswidth(const wchar_t *str, size_t n) -{ - return mk_wcswidth(str, n); -} +int fish_wcswidth(const wchar_t *str, size_t n) { return mk_wcswidth(str, n); } /* * This is an implementation of wcwidth() and wcswidth() (defined in @@ -1331,21 +1067,17 @@ int fish_wcswidth(const wchar_t *str, size_t n) * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c */ -struct interval -{ +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) -{ +// Auxiliary function for binary search in interval table. +static int bisearch(wchar_t ucs, const struct interval *table, int max) { int min = 0; - if (ucs < table[0].first || ucs > table[max].last) - return 0; - while (max >= min) - { + if (ucs < table[0].first || ucs > table[max].last) return 0; + while (max >= min) { int mid = (min + max) / 2; if (ucs > table[mid].last) min = mid + 1; @@ -1358,135 +1090,104 @@ static int bisearch(wchar_t ucs, const struct interval *table, int max) return 0; } +// The following two functions define the column width of an ISO 10646 character as follows: +// +// - The null character (U+0000) has a column width of 0. +// +// - Other C0/C1 control characters and DEL will lead to a return +// value of -1. +// +// - Non-spacing and enclosing combining characters (general +// category code Mn or Me in the Unicode database) have a +// column width of 0. +// +// - SOFT HYPHEN (U+00AD) has a column width of 1. +// +// - Other format characters (general category code Cf in the Unicode +// database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. +// +// - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) +// have a column width of 0. +// +// - Spacing characters in the East Asian Wide (W) or East Asian +// Full-width (F) category as defined in Unicode Technical +// Report #11 have a column width of 2. +// +// - All remaining characters (including all printable +// ISO 8859-1 and WGL4 characters, Unicode control characters, +// etc.) have a column width of 1. +// +// This implementation assumes that wchar_t characters are encoded +// in ISO 10646. +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}}; -/* The following two functions define the column width of an ISO 10646 - * character as follows: - * - * - The null character (U+0000) has a column width of 0. - * - * - Other C0/C1 control characters and DEL will lead to a return - * value of -1. - * - * - Non-spacing and enclosing combining characters (general - * category code Mn or Me in the Unicode database) have a - * column width of 0. - * - * - SOFT HYPHEN (U+00AD) has a column width of 1. - * - * - Other format characters (general category code Cf in the Unicode - * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. - * - * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) - * have a column width of 0. - * - * - Spacing characters in the East Asian Wide (W) or East Asian - * Full-width (F) category as defined in Unicode Technical - * Report #11 have a column width of 2. - * - * - All remaining characters (including all printable - * ISO 8859-1 and WGL4 characters, Unicode control characters, - * etc.) have a column width of 1. - * - * This implementation assumes that wchar_t characters are encoded - * in ISO 10646. - */ + // Test for 8-bit control characters. + if (ucs == 0) return 0; + if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) return -1; -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 } - }; + // Binary search in table of non-spacing characters. + if (bisearch(ucs, combining, sizeof(combining) / sizeof(struct interval) - 1)) return 0; - /* 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; - - /* 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))); + // 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))); } -static int mk_wcswidth(const wchar_t *pwcs, size_t n) -{ +static int mk_wcswidth(const wchar_t *pwcs, size_t n) { int width = 0; - for (size_t i=0; i < n; i++) - { - if (pwcs[i] == L'\0') - break; + for (size_t i = 0; i < n; i++) { + if (pwcs[i] == L'\0') break; int w = mk_wcwidth(pwcs[i]); - if (w < 0) - { + if (w < 0) { width = -1; break; } @@ -1495,4 +1196,4 @@ static int mk_wcswidth(const wchar_t *pwcs, size_t n) return width; } -#endif // HAVE_BROKEN_WCWIDTH +#endif // HAVE_BROKEN_WCWIDTH diff --git a/src/fallback.h b/src/fallback.h index ae48e2324..ca7a671b5 100644 --- a/src/fallback.h +++ b/src/fallback.h @@ -7,8 +7,8 @@ // compiling several modules that include this header because they use symbols which are defined as // macros in . // IWYU pragma: no_include -#include #include +#include #include // The following include must be kept despite what IWYU says. That's because of the interaction // between the weak linking of `wcsdup` and `wcscasecmp` via `#define`s below and the declarations @@ -19,24 +19,19 @@ #include // IWYU pragma: keep #endif -/** fish's internal versions of wcwidth and wcswidth, which can use an internal implementation if the system one is busted. */ +/// fish's internal versions of wcwidth and wcswidth, which can use an internal implementation if +/// the system one is busted. int fish_wcwidth(wchar_t wc); int fish_wcswidth(const wchar_t *str, size_t n); #ifndef WCHAR_MAX -/** - This _should_ be defined by wchar.h, but e.g. OpenBSD doesn't. -*/ +/// This _should_ be defined by wchar.h, but e.g. OpenBSD doesn't. #define WCHAR_MAX INT_MAX #endif -/** - Under curses, tputs expects an int (*func)(char) as its last - parameter, but in ncurses, tputs expects a int (*func)(int) as its - last parameter. tputs_arg_t is defined to always be what tputs - expects. Hopefully. -*/ - +/// Under curses, tputs expects an int (*func)(char) as its last parameter, but in ncurses, tputs +/// expects a int (*func)(int) as its last parameter. tputs_arg_t is defined to always be what tputs +/// expects. Hopefully. #ifdef NCURSES_VERSION typedef int tputs_arg_t; #else @@ -52,103 +47,60 @@ typedef char tputs_arg_t; #endif #ifndef HAVE_WINSIZE -/** - Structure used to get the size of a terminal window - */ -struct winsize -{ - /** - Number of rows - */ +/// Structure used to get the size of a terminal window. +struct winsize { + /// Number of rows. unsigned short ws_row; - /** - Number of columns - */ + /// Number of columns. unsigned short ws_col; -} -; +}; #endif #ifdef TPUTS_KLUDGE -/** - Linux on PPC seems to have a tputs implementation that sometimes - behaves strangely. This fallback seems to fix things. -*/ +/// Linux on PPC seems to have a tputs implementation that sometimes behaves strangely. This +/// fallback seems to fix things. int tputs(const char *str, int affcnt, int (*fish_putc)(tputs_arg_t)); #endif #ifdef TPARM_SOLARIS_KLUDGE - -/** - Solaris tparm has a set fixed of paramters in it's curses implementation, - work around this here. -*/ - +/// Solaris tparm has a set fixed of paramters in it's curses implementation, work around this here. #define tparm tparm_solaris_kludge char *tparm_solaris_kludge(char *str, ...); - #endif #ifndef HAVE_FWPRINTF - -/** - Print formated string. Some operating systems (Like NetBSD) do not - have wide string formating functions. Therefore we implement our - own. Not at all complete. Supports wide and narrow characters, - strings and decimal numbers, position (%n), field width and - precision. -*/ +/// Print formated string. Some operating systems (Like NetBSD) do not have wide string formating +/// functions. Therefore we implement our own. Not at all complete. Supports wide and narrow +/// characters, strings and decimal numbers, position (%n), field width and precision. int fwprintf(FILE *f, const wchar_t *format, ...); - -/** - Print formated string. Some operating systems (Like NetBSD) do not - have wide string formating functions. Therefore we define our - own. Not at all complete. Supports wide and narrow characters, - strings and decimal numbers, position (%n), field width and - precision. -*/ +/// Print formated string. Some operating systems (Like NetBSD) do not have wide string formating +/// functions. Therefore we define our own. Not at all complete. Supports wide and narrow +/// characters, strings and decimal numbers, position (%n), field width and precision. int swprintf(wchar_t *str, size_t l, const wchar_t *format, ...); -/** - Print formated string. Some operating systems (Like NetBSD) do not - have wide string formating functions. Therefore we define our - own. Not at all complete. Supports wide and narrow characters, - strings and decimal numbers, position (%n), field width and - precision. -*/ +/// Print formated string. Some operating systems (Like NetBSD) do not have wide string formating +/// functions. Therefore we define our own. Not at all complete. Supports wide and narrow +/// characters, strings and decimal numbers, position (%n), field width and precision. int wprintf(const wchar_t *format, ...); -/** - Print formated string. Some operating systems (Like NetBSD) do not - have wide string formating functions. Therefore we define our - own. Not at all complete. Supports wide and narrow characters, - strings and decimal numbers, position (%n), field width and - precision. -*/ +/// Print formated string. Some operating systems (Like NetBSD) do not have wide string formating +/// functions. Therefore we define our own. Not at all complete. Supports wide and narrow +/// characters, strings and decimal numbers, position (%n), field width and precision. int vwprintf(const wchar_t *filter, va_list va); -/** - Print formated string. Some operating systems (Like NetBSD) do not - have wide string formating functions. Therefore we define our - own. Not at all complete. Supports wide and narrow characters, - strings and decimal numbers, position (%n), field width and - precision. -*/ +/// Print formated string. Some operating systems (Like NetBSD) do not have wide string formating +/// functions. Therefore we define our own. Not at all complete. Supports wide and narrow +/// characters, strings and decimal numbers, position (%n), field width and precision. int vfwprintf(FILE *f, const wchar_t *filter, va_list va); -/** - Print formated string. Some operating systems (Like NetBSD) do not - have wide string formating functions. Therefore we define our - own. Not at all complete. Supports wide and narrow characters, - strings and decimal numbers, position (%n), field width and - precision. -*/ +/// Print formated string. Some operating systems (Like NetBSD) do not have wide string formating +/// functions. Therefore we define our own. Not at all complete. Supports wide and narrow +/// characters, 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); - #endif #ifndef HAVE_FGETWC @@ -162,36 +114,28 @@ wint_t fputwc(wchar_t wc, FILE *stream); #endif #ifndef HAVE_WCSTOK - -/** - Fallback implementation of wcstok. Uses code borrowed from glibc. -*/ +/// Fallback implementation of wcstok. Uses code borrowed from glibc. wchar_t *wcstok(wchar_t *wcs, const wchar_t *delim, wchar_t **ptr); - #endif #ifndef HAVE_WCWIDTH - -/** - Return the number of columns used by a character. This is a libc - function, but the prototype for this function is missing in some libc - implementations. - - Fish has a fallback implementation in case the implementation is - missing altogether. In locales without a native wcwidth, Unicode - is probably so broken that it isn't worth trying to implement a - real wcwidth. Therefore, the fallback wcwidth assumes any printing - character takes up one column and anything else uses 0 columns. -*/ +/// Return the number of columns used by a character. This is a libc function, but the prototype for +/// this function is missing in some libc implementations. +/// +/// Fish has a fallback implementation in case the implementation is missing altogether. In locales +/// without a native wcwidth, Unicode is probably so broken that it isn't worth trying to implement +/// a 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); - #endif - -/** On OS X, use weak linking for wcsdup and wcscasecmp. Weak linking allows you to call the function only if it exists at runtime. You can detect it by testing the function pointer against NULL. To avoid making the callers do that, redefine wcsdup to wcsdup_use_weak, and likewise with wcscasecmp. This lets us use the same binary on SnowLeopard (10.6) and Lion+ (10.7), even though these functions only exist on 10.7+. - - On other platforms, use what's detected at build time. -*/ +/// On OS X, use weak linking for wcsdup and wcscasecmp. Weak linking allows you to call the +/// function only if it exists at runtime. You can detect it by testing the function pointer against +/// NULL. To avoid making the callers do that, redefine wcsdup to wcsdup_use_weak, and likewise with +/// wcscasecmp. This lets us use the same binary on SnowLeopard (10.6) and Lion+ (10.7), even though +/// these functions only exist on 10.7+. +/// +/// 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 *); @@ -203,204 +147,127 @@ int wcsncasecmp_use_weak(const wchar_t *s1, const wchar_t *s2, size_t n); #else #ifndef HAVE_WCSDUP - -/** - Create a duplicate string. Wide string version of strdup. Will - automatically exit if out of memory. -*/ +/// Create a duplicate string. Wide string version of strdup. Will automatically exit if out of +/// memory. wchar_t *wcsdup(const wchar_t *in); - #endif #ifndef HAVE_WCSCASECMP -/** - Case insensitive string compare function. Wide string version of - strcasecmp. - - This implementation of wcscasecmp does not take into account - esoteric locales where uppercase and lowercase do not cleanly - transform between each other. Hopefully this should be fine since - fish only uses this function with one of the strings supplied by - fish and guaranteed to be a sane, english word. Using wcscasecmp on - a user-supplied string should be considered a bug. -*/ +/// Case insensitive string compare function. Wide string version of strcasecmp. +/// +/// This implementation of wcscasecmp does not take into account esoteric locales where uppercase +/// and lowercase do not cleanly transform between each other. Hopefully this should be fine since +/// fish only uses this function with one of the strings supplied by 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); - #endif -#endif //__APPLE__ - +#endif //__APPLE__ #ifndef HAVE_WCSLEN - -/** - Fallback for wclsen. Returns the length of the specified string. -*/ +/// Fallback for wclsen. Returns the length of the specified string. size_t wcslen(const wchar_t *in); - #endif - #ifndef HAVE_WCSNCASECMP - -/** - Case insensitive string compare function. Wide string version of - strncasecmp. - - This implementation of wcsncasecmp does not take into account - esoteric locales where uppercase and lowercase do not cleanly - transform between each other. Hopefully this should be fine since - fish only uses this function with one of the strings supplied by - fish and guaranteed to be a sane, english word. Using wcsncasecmp on - a user-supplied string should be considered a bug. -*/ +/// Case insensitive string compare function. Wide string version of strncasecmp. +/// +/// This implementation of wcsncasecmp does not take into account esoteric locales where uppercase +/// and lowercase do not cleanly transform between each other. Hopefully this should be fine since +/// fish only uses this function with one of the strings supplied by 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, size_t count); -/** - Returns a newly allocated wide character string wich is a copy of - the string in, but of length c or shorter. The returned string is - always null terminated, and the null is not included in the string - length. -*/ - +/// Returns a newly allocated wide character string wich is a copy of the string in, but of length c +/// or shorter. The returned string is always null terminated, and the null is not included in the +/// string length. #endif #ifndef HAVE_WCSNDUP - -/** - Fallback for wcsndup function. Returns a copy of \c in, truncated - to a maximum length of \c c. -*/ +/// 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); - #endif -/** - Converts from wide char to digit in the specified base. If d is not - 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. -*/ +/// Converts from wide char to digit in the specified base. If d is not 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); #ifndef HAVE_WCSTOL - -/** - Fallback implementation. Convert a wide character string to a - number in the specified base. This functions is the wide character - string equivalent of strtol. For bases of 10 or lower, 0..9 are - used to represent numbers. For bases below 36, a-z and A-Z are used - to represent numbers higher than 9. Higher bases than 36 are not - supported. -*/ -long wcstol(const wchar_t *nptr, - wchar_t **endptr, - int base); - +/// Fallback implementation. Convert a wide character string to a number in the specified base. This +/// functions is the wide character string equivalent of strtol. For bases of 10 or lower, 0..9 are +/// used to represent numbers. For bases below 36, a-z and A-Z are used to represent numbers higher +/// than 9. Higher bases than 36 are not supported. +long wcstol(const wchar_t *nptr, wchar_t **endptr, int base); #endif #ifndef HAVE_WCSLCAT - -/** - Appends src to string dst of size siz (unlike wcsncat, siz is the - full size of dst, not space left). At most siz-1 characters will be - copied. Always NUL terminates (unless siz <= wcslen(dst)). Returns - wcslen(src) + MIN(siz, wcslen(initial dst)). If retval >= siz, - truncation occurred. - - This is the OpenBSD strlcat function, modified for wide characters, - and renamed to reflect this change. - -*/ +/// Appends src to string dst of size siz (unlike wcsncat, siz is the full size of dst, not space +/// left). At most siz-1 characters will be copied. Always NUL terminates (unless siz <= +/// wcslen(dst)). Returns wcslen(src) + MIN(siz, wcslen(initial dst)). If retval >= siz, +/// truncation occurred. +/// +/// This is the OpenBSD strlcat function, modified for wide characters, and renamed to reflect this +/// change. size_t wcslcat(wchar_t *dst, const wchar_t *src, size_t siz); - #endif #ifndef HAVE_WCSLCPY - -/** - Copy src to string dst of size siz. At most siz-1 characters will - be copied. Always NUL terminates (unless siz == 0). Returns - wcslen(src); if retval >= siz, truncation occurred. - - This is the OpenBSD strlcpy function, modified for wide characters, - and renamed to reflect this change. -*/ +/// Copy src to string dst of size siz. At most siz-1 characters will be copied. Always NUL +/// terminates (unless siz == 0). Returns wcslen(src); if retval >= siz, truncation occurred. +/// +/// 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); - #endif #ifndef HAVE_LRAND48_R - -/** - Datastructure for the lrand48_r fallback implementation. -*/ -struct drand48_data -{ - /** - Seed value - */ +/// Datastructure for the lrand48_r fallback implementation. +struct drand48_data { unsigned int seed; -} -; +}; -/** - Fallback implementation of lrand48_r. Internally uses rand_r, so it is pretty weak. -*/ +/// Fallback implementation of lrand48_r. Internally uses rand_r, so it is pretty weak. int lrand48_r(struct drand48_data *buffer, long int *result); -/** - Fallback implementation of srand48_r, the seed function for lrand48_r. -*/ +/// Fallback implementation of srand48_r, the seed function for lrand48_r. int srand48_r(long int seedval, struct drand48_data *buffer); - #endif #ifndef HAVE_FUTIMES - int futimes(int fd, const struct timeval *times); - #endif -/* autoconf may fail to detect gettext (645), so don't define a function call gettext or we'll get build errors */ +// autoconf may fail to detect gettext (645), so don't define a function call gettext or we'll get +// build errors. -/** Cover for gettext() */ -char * fish_gettext(const char * msgid); +/// Cover for gettext(). +char *fish_gettext(const char *msgid); -/** Cover for bindtextdomain() */ -char * fish_bindtextdomain(const char * domainname, const char * dirname); +/// Cover for bindtextdomain(). +char *fish_bindtextdomain(const char *domainname, const char *dirname); -/** Cover for textdomain() */ -char * fish_textdomain(const char * domainname); +/// Cover for textdomain(). +char *fish_textdomain(const char *domainname); -/* Cover for dcgettext */ -char * fish_dcgettext(const char * domainname, const char * msgid, int category); +/// Cover for dcgettext. +char *fish_dcgettext(const char *domainname, const char *msgid, int category); #ifndef HAVE__NL_MSG_CAT_CNTR - -/** - Some gettext implementation use have this variable, and by - increasing it, one can tell the system that the translations need - to be reloaded. -*/ +/// Some gettext implementation use have this variable, and by increasing it, one can tell the +/// system that the translations need to be reloaded. extern int _nl_msg_cat_cntr; - #endif - #ifndef HAVE_KILLPG -/** - Send specified signal to specified process group. - */ +/// Send specified signal to specified process group. int killpg(int pgr, int sig); #endif #ifndef HAVE_SYSCONF - #define _SC_ARG_MAX 1 long sysconf(int name); - #endif #ifndef HAVE_NAN double nan(char *tagp); #endif - #endif From 075811e58836bc82de3052d064e9f0aa9335b6e7 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sat, 30 Apr 2016 18:37:19 -0700 Subject: [PATCH 185/363] restyle fish modules to match project style Reduces lint errors from 60 to 60 (-0%). Line count from 5599 to 4925 (-12%). Another step in resolving issue #2902. --- src/fish.cpp | 442 ++--- src/fish_indent.cpp | 365 ++--- src/fish_tests.cpp | 3714 ++++++++++++++++++------------------------ src/fish_version.cpp | 18 +- src/fish_version.h | 5 +- 5 files changed, 1935 insertions(+), 2609 deletions(-) diff --git a/src/fish.cpp b/src/fish.cpp index 128a529c4..dcad6c8b3 100644 --- a/src/fish.cpp +++ b/src/fish.cpp @@ -1,3 +1,4 @@ +// The main loop of the fish program. /* Copyright (C) 2005-2008 Axel Liljencrantz @@ -14,85 +15,79 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ - -/** \file fish.c - The main loop of fish. -*/ #include "config.h" #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include -#include // IWYU pragma: keep -#include -#include +#include +#include #include -#include +#include #include +#include +#include +#include +#include +#include // IWYU pragma: keep +#include +#include +#include +#include +#include +#include +#include -#include "fallback.h" // IWYU pragma: keep -#include "common.h" -#include "reader.h" #include "builtin.h" -#include "function.h" -#include "wutil.h" // IWYU pragma: keep +#include "common.h" #include "env.h" -#include "proc.h" -#include "parser.h" -#include "expand.h" #include "event.h" -#include "history.h" -#include "path.h" -#include "input.h" -#include "io.h" +#include "expand.h" +#include "fallback.h" // IWYU pragma: keep #include "fish_version.h" +#include "function.h" +#include "history.h" +#include "input.h" #include "input_common.h" +#include "io.h" +#include "parser.h" +#include "path.h" +#include "proc.h" +#include "reader.h" #include "wildcard.h" +#include "wutil.h" // IWYU pragma: keep // PATH_MAX may not exist. #ifndef PATH_MAX #define PATH_MAX 4096 #endif -/* If we are doing profiling, the filename to output to */ +/// If we are doing profiling, the filename to output to. static const char *s_profiling_output_filename = NULL; -static bool has_suffix(const std::string &path, const char *suffix, bool ignore_case) -{ +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 */ -static bool get_realpath(std::string &path) -{ +/// Modifies the given path by calling realpath. Returns true if realpath succeeded, false +/// otherwise. +static bool get_realpath(std::string &path) { char buff[PATH_MAX], *ptr; - if ((ptr = realpath(path.c_str(), buff))) - { + if ((ptr = realpath(path.c_str(), buff))) { path = ptr; } return ptr != NULL; } -/* OS X function for getting the executable path */ +// OS X function for getting the executable path. extern "C" { - int _NSGetExecutablePath(char* buf, uint32_t* bufsize); +int _NSGetExecutablePath(char *buf, uint32_t *bufsize); } -// Return the path to the current executable. This needs to be realpath'd. -static std::string get_executable_path(const char *argv0) -{ +/// Return the path to the current executable. This needs to be realpath'd. +static std::string get_executable_path(const char *argv0) { char buff[PATH_MAX]; #if __APPLE__ @@ -121,35 +116,32 @@ static std::string get_executable_path(const char *argv0) return std::string(argv0 ? argv0 : ""); } - -static struct config_paths_t determine_config_directory_paths(const char *argv0) -{ +static struct config_paths_t determine_config_directory_paths(const char *argv0) { struct config_paths_t paths; bool done = false; std::string exec_path = get_executable_path(argv0); - if (get_realpath(exec_path)) - { + 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) - { + // 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) { const char *suffix = "/Contents/MacOS/fish"; const size_t suffixlen = strlen(suffix); - if (has_suffix(exec_path, suffix, true)) - { - /* Looks like we're a bundle. Cut the string at the / prefixing /Contents... and then the rest */ + if (has_suffix(exec_path, suffix, true)) { + // Looks like we're a bundle. Cut the string at the / prefixing /Contents... and + // then the rest. 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 */ + // 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) */ + // 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")); @@ -158,18 +150,13 @@ static struct config_paths_t determine_config_directory_paths(const char *argv0) } #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! - */ + if (!done) { + // The next check is that we are in a reloctable directory tree like this: + // bin/fish + // etc/fish + // share/fish const char *suffix = "/bin/fish"; - if (has_suffix(exec_path, suffix, false)) - { + if (has_suffix(exec_path, suffix, false)) { wcstring base_path = str2wcstring(exec_path); base_path.resize(base_path.size() - strlen(suffix)); @@ -178,13 +165,12 @@ static struct config_paths_t determine_config_directory_paths(const char *argv0) paths.doc = base_path + L"/share/doc/fish"; paths.bin = base_path + L"/bin"; - /* Check only that the data and sysconf directories exist. Handle the doc directories separately */ + // Check only that the data and sysconf directories exist. Handle the doc + // directories separately. struct stat buf; - if (0 == wstat(paths.data, &buf) && 0 == wstat(paths.sysconf, &buf)) - { - /* The docs dir may not exist; in that case fall back to the compiled in path */ - if (0 != wstat(paths.doc, &buf)) - { + if (0 == wstat(paths.data, &buf) && 0 == wstat(paths.sysconf, &buf)) { + // The docs dir may not exist; in that case fall back to the compiled in path. + if (0 != wstat(paths.doc, &buf)) { paths.doc = L"" DOCDIR; } done = true; @@ -193,9 +179,8 @@ static struct config_paths_t determine_config_directory_paths(const char *argv0) } } - if (! done) - { - /* Fall back to what got compiled in. */ + if (!done) { + // Fall back to what got compiled in. paths.data = L"" DATADIR "/fish"; paths.sysconf = L"" SYSCONFDIR "/fish"; paths.doc = L"" DOCDIR; @@ -208,21 +193,19 @@ static struct config_paths_t determine_config_directory_paths(const char *argv0) } // Source the file config.fish in the given directory. -static void source_config_in_directory(const wcstring &dir) -{ - // If the config.fish file doesn't exist or isn't readable silently return. - // Fish versions up thru 2.2.0 would instead try to source the file with - // stderr redirected to /dev/null to deal with that possibility. +static void source_config_in_directory(const wcstring &dir) { + // If the config.fish file doesn't exist or isn't readable silently return. Fish versions up + // thru 2.2.0 would instead try to source the file with stderr redirected to /dev/null to deal + // with that possibility. // - // This introduces a race condition since the readability of the file can - // change between this test and the execution of the 'source' command. - // However, that is not a security problem in this context so we ignore it. + // This introduces a race condition since the readability of the file can change between this + // test and the execution of the 'source' command. However, that is not a security problem in + // this context so we ignore it. const wcstring config_pathname = dir + L"/config.fish"; const wcstring escaped_dir = escape_string(dir, ESCAPE_ALL); const wcstring escaped_pathname = escaped_dir + L"/config.fish"; if (waccess(config_pathname, R_OK) != 0) { - debug(2, L"not sourcing %ls (not readable or does not exist)", - escaped_pathname.c_str()); + debug(2, L"not sourcing %ls (not readable or does not exist)", escaped_pathname.c_str()); return; } debug(2, L"sourcing %ls", escaped_pathname.c_str()); @@ -234,21 +217,14 @@ static void source_config_in_directory(const wcstring &dir) parser.set_is_within_fish_initialization(false); } -static int try_connect_socket(std::string &name) -{ +static int try_connect_socket(std::string &name) { int s, r, ret = -1; - /** Connect to a DGRAM socket rather than the expected STREAM. - This avoids any notification to a remote socket that we have connected, - preventing any surprising behaviour. - If the connection fails with EPROTOTYPE, the connection is probably a - STREAM; if it succeeds or fails any other way, there is no cause for - alarm. - With thanks to Andrew Lutomirski - */ - - if ((s = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) - { + /// Connect to a DGRAM socket rather than the expected STREAM. This avoids any notification to a + /// remote socket that we have connected, preventing any surprising behaviour. If the connection + /// fails with EPROTOTYPE, the connection is probably a STREAM; if it succeeds or fails any + /// other way, there is no cause for alarm. With thanks to Andrew Lutomirski + if ((s = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) { wperror(L"socket"); return -1; } @@ -261,8 +237,7 @@ static int try_connect_socket(std::string &name) r = connect(s, (struct sockaddr *)&local, sizeof local); - if (r == -1 && errno == EPROTOTYPE) - { + if (r == -1 && errno == EPROTOTYPE) { ret = 0; } @@ -270,24 +245,19 @@ static int try_connect_socket(std::string &name) return ret; } -/** - Check for a running fishd from old versions and warn about not being able - to share variables. - https://github.com/fish-shell/fish-shell/issues/1730 -*/ -static void check_running_fishd() -{ - /* There are two paths to check: - $FISHD_SOCKET_DIR/fishd.socket.$USER or /tmp/fishd.socket.$USER - - referred to as the "old socket" - $XDG_RUNTIME_DIR/fishd.socket or /tmp/fish.$USER/fishd.socket - - referred to as the "new socket" - All existing versions of fish attempt to create the old socket, but - failure in newer versions is not treated as critical, so both need - to be checked. */ +/// Check for a running fishd from old versions and warn about not being able to share variables. +/// https://github.com/fish-shell/fish-shell/issues/1730 +static void check_running_fishd() { + // There are two paths to check: + // $FISHD_SOCKET_DIR/fishd.socket.$USER or /tmp/fishd.socket.$USER + // - referred to as the "old socket" + // $XDG_RUNTIME_DIR/fishd.socket or /tmp/fish.$USER/fishd.socket + // - referred to as the "new socket" + // All existing versions of fish attempt to create the old socket, but + // failure in newer versions is not treated as critical, so both need + // to be checked. const char *uname = getenv("USER"); - if (uname == NULL) - { + if (uname == NULL) { const struct passwd *pw = getpwuid(getuid()); uname = pw->pw_name; } @@ -295,12 +265,9 @@ static void check_running_fishd() const char *dir_old_socket = getenv("FISHD_SOCKET_DIR"); std::string path_old_socket; - if (dir_old_socket == NULL) - { + if (dir_old_socket == NULL) { path_old_socket = "/tmp/"; - } - else - { + } else { path_old_socket.append(dir_old_socket); } @@ -309,146 +276,107 @@ static void check_running_fishd() const char *dir_new_socket = getenv("XDG_RUNTIME_DIR"); std::string path_new_socket; - if (dir_new_socket == NULL) - { + if (dir_new_socket == NULL) { path_new_socket = "/tmp/fish."; path_new_socket.append(uname); path_new_socket.push_back('/'); - } - else - { + } else { path_new_socket.append(dir_new_socket); } path_new_socket.append("fishd.socket"); - if (try_connect_socket(path_old_socket) == 0 || try_connect_socket(path_new_socket) == 0) - { - debug(1, _(L"Old versions of fish appear to be running. You will not be able to share variable values between old and new fish sessions. For best results, restart all running instances of fish.")); + if (try_connect_socket(path_old_socket) == 0 || try_connect_socket(path_new_socket) == 0) { + debug(1, _(L"Old versions of fish appear to be running. You will not be able to share " + L"variable values between old and new fish sessions. For best results, restart " + L"all running instances of fish.")); } - } -/** - Parse init files. exec_path is the path of fish executable as determined by argv[0]. -*/ -static int read_init(const struct config_paths_t &paths) -{ +/// Parse init files. exec_path is the path of fish executable as determined by argv[0]. +static int read_init(const struct config_paths_t &paths) { source_config_in_directory(paths.data); source_config_in_directory(paths.sysconf); - /* We need to get the configuration directory before we can source the user configuration file. 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. + // If path_get_config returns false then we have no configuration directory and no custom config + // to load. wcstring config_dir; - if (path_get_config(config_dir)) - { + if (path_get_config(config_dir)) { source_config_in_directory(config_dir); } return 1; } -/** - Parse the argument list, return the index of the first non-switch - arguments. - */ -static int fish_parse_opt(int argc, char **argv, std::vector *cmds) -{ +/// Parse the argument list, return the index of the first non-switch arguments. +static int fish_parse_opt(int argc, char **argv, std::vector *cmds) { const char *short_opts = "+hilnvc:p:d:"; - const struct option long_opts[] = - { - { "command", required_argument, NULL, 'c' }, - { "debug-level", required_argument, NULL, 'd' }, - { "interactive", no_argument, NULL, 'i' } , - { "login", no_argument, NULL, 'l' }, - { "no-execute", no_argument, NULL, 'n' }, - { "profile", required_argument, NULL, 'p' }, - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'v' }, - { NULL, 0, NULL, 0 } - }; + const struct option long_opts[] = {{"command", required_argument, NULL, 'c'}, + {"debug-level", required_argument, NULL, 'd'}, + {"interactive", no_argument, NULL, 'i'}, + {"login", no_argument, NULL, 'l'}, + {"no-execute", no_argument, NULL, 'n'}, + {"profile", required_argument, NULL, 'p'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'v'}, + {NULL, 0, NULL, 0}}; int opt; - while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) - { - switch (opt) - { - case 0: - { + while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) { + switch (opt) { + case 0: { fwprintf(stderr, _(L"getopt_long() unexpectedly returned zero\n")); exit(127); } - - case 'c': - { + case 'c': { cmds->push_back(optarg); break; } - - case 'd': - { + case 'd': { char *end; long tmp; errno = 0; tmp = strtol(optarg, &end, 10); - if (tmp >= 0 && tmp <=10 && !*end && !errno) - { + if (tmp >= 0 && tmp <= 10 && !*end && !errno) { debug_level = (int)tmp; - } - else - { - fwprintf(stderr, _(L"Invalid value '%s' for debug level switch"), - optarg); + } else { + fwprintf(stderr, _(L"Invalid value '%s' for debug level switch"), optarg); exit(1); } break; } - - case 'h': - { + case 'h': { cmds->push_back("__fish_print_help fish"); break; } - - case 'i': - { + case 'i': { is_interactive_session = 1; break; } - - case 'l': - { + case 'l': { is_login = 1; break; } - - case 'n': - { + case 'n': { no_exec = 1; break; } - - case 'p': - { + case 'p': { s_profiling_output_filename = optarg; g_profiling_active = true; break; } - - case 'v': - { - fwprintf(stdout, _(L"%s, version %s\n"), PACKAGE_NAME, - get_fish_version()); + case 'v': { + fwprintf(stdout, _(L"%s, version %s\n"), PACKAGE_NAME, get_fish_version()); exit(0); } - - default: - { + default: { // We assume getopt_long() has already emitted a diagnostic msg. exit(1); } - } } @@ -458,51 +386,43 @@ static int fish_parse_opt(int argc, char **argv, std::vector *cmds) // We are an interactive session if we have not been given an explicit // command or file to execute and stdin is a tty. Note that the -i or // --interactive options also force interactive mode. - if (cmds->size() == 0 && optind == argc && isatty(STDIN_FILENO)) - { + if (cmds->size() == 0 && optind == argc && isatty(STDIN_FILENO)) { is_interactive_session = 1; } return optind; } -int main(int argc, char **argv) -{ - int res=1; - int my_optind=0; +int main(int argc, char **argv) { + int res = 1; + int my_optind = 0; // We can't do this at compile time due to the use of enum symbols. - assert(EXPAND_SENTINAL >= EXPAND_RESERVED_BASE && - EXPAND_SENTINAL <= EXPAND_RESERVED_END); - assert(ANY_SENTINAL >= WILDCARD_RESERVED_BASE && - ANY_SENTINAL <= WILDCARD_RESERVED_END); - assert(R_SENTINAL >= INPUT_COMMON_BASE && - R_SENTINAL <= INPUT_COMMON_END); + assert(EXPAND_SENTINAL >= EXPAND_RESERVED_BASE && EXPAND_SENTINAL <= EXPAND_RESERVED_END); + assert(ANY_SENTINAL >= WILDCARD_RESERVED_BASE && ANY_SENTINAL <= WILDCARD_RESERVED_END); + assert(R_SENTINAL >= INPUT_COMMON_BASE && R_SENTINAL <= INPUT_COMMON_END); set_main_thread(); setup_fork_guards(); wsetlocale(LC_ALL, L""); - program_name=L"fish"; + program_name = L"fish"; - //struct stat tmp; - //stat("----------FISH_HIT_MAIN----------", &tmp); + // struct stat tmp; + // stat("----------FISH_HIT_MAIN----------", &tmp); std::vector cmds; my_optind = fish_parse_opt(argc, argv, &cmds); - /* - No-exec is prohibited when in interactive mode - */ - if (is_interactive_session && no_exec) - { + // 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; } - /* Only save (and therefore restore) the fg process group if we are interactive. See #197, #1002 */ - if (is_interactive_session) - { + // Only save (and therefore restore) the fg process group if we are interactive. See issues + // #197 and #1002. + if (is_interactive_session) { save_term_foreground_process_group(); } @@ -515,63 +435,48 @@ int main(int argc, char **argv) env_init(&paths); reader_init(); history_init(); - /* For setcolor to support term256 in config.fish (#1022) */ + // For set_color to support term256 in config.fish (issue #1022). update_fish_color_support(); parser_t &parser = parser_t::principal_parser(); - if (g_log_forks) - printf("%d: g_fork_count: %d\n", __LINE__, g_fork_count); + if (g_log_forks) printf("%d: g_fork_count: %d\n", __LINE__, g_fork_count); const io_chain_t empty_ios; - if (read_init(paths)) - { - /* Stomp the exit status of any initialization commands (#635) */ + if (read_init(paths)) { + // Stomp the exit status of any initialization commands (issue #635). proc_set_last_status(STATUS_BUILTIN_OK); - /* Run the commands specified as arguments, if any */ - if (! cmds.empty()) - { - /* Do something nasty to support OpenSUSE assuming we're bash. This may modify cmds. */ - if (is_login) - { + // Run the commands specified as arguments, if any. + if (!cmds.empty()) { + // Do something nasty to support OpenSUSE assuming we're bash. This may modify cmds. + if (is_login) { fish_xdm_login_hack_hack_hack_hack(&cmds, argc - my_optind, argv + my_optind); } - for (size_t i=0; i < cmds.size(); i++) - { + for (size_t i = 0; i < cmds.size(); i++) { const wcstring cmd_wcs = str2wcstring(cmds.at(i)); res = parser.eval(cmd_wcs, empty_ios, TOP); } reader_exit(0, 0); - } - else if (my_optind == argc) - { + } else if (my_optind == argc) { // Interactive mode check_running_fishd(); res = reader_read(STDIN_FILENO, empty_ios); - } - else - { - char *file = *(argv+(my_optind++)); + } else { + char *file = *(argv + (my_optind++)); int fd = open(file, O_RDONLY); - if (fd == -1) - { + if (fd == -1) { perror(file); - } - else - { - // OK to not do this atomically since we cannot have gone multithreaded yet + } else { + // 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; char **ptr; int i; - for (i=1,ptr = argv+my_optind; *ptr; i++, ptr++) - { - if (i != 1) - sb.append(ARRAY_SEP_STR); + for (i = 1, ptr = argv + my_optind; *ptr; i++, ptr++) { + if (i != 1) sb.append(ARRAY_SEP_STR); sb.append(str2wcstring(*ptr)); } @@ -584,11 +489,10 @@ int main(int argc, char **argv) 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")); + if (res) { + debug(1, _(L"Error while reading file %ls\n"), reader_current_filename() + ? reader_current_filename() + : _(L"Standard input")); } reader_pop_current_filename(); } @@ -602,8 +506,7 @@ int main(int argc, char **argv) restore_term_mode(); restore_term_foreground_process_group(); - if (g_profiling_active) - { + if (g_profiling_active) { parser.emit_profiling(s_profiling_output_filename); } @@ -613,9 +516,8 @@ int main(int argc, char **argv) reader_destroy(); event_destroy(); - if (g_log_forks) - printf("%d: g_fork_count: %d\n", __LINE__, g_fork_count); + if (g_log_forks) printf("%d: g_fork_count: %d\n", __LINE__, g_fork_count); exit_without_destructors(exit_status); - return EXIT_FAILURE; //above line should always exit + return EXIT_FAILURE; // above line should always exit } diff --git a/src/fish_indent.cpp b/src/fish_indent.cpp index fbe9b13cd..534437991 100644 --- a/src/fish_indent.cpp +++ b/src/fish_indent.cpp @@ -15,30 +15,30 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#include -#include -#include -#include -#include #include +#include #include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include #include "color.h" -#include "highlight.h" -#include "parse_constants.h" -#include "wutil.h" // IWYU pragma: keep #include "common.h" -#include "output.h" #include "env.h" +#include "fish_version.h" +#include "highlight.h" #include "input.h" +#include "output.h" +#include "parse_constants.h" #include "parse_tree.h" #include "print_help.h" -#include "fish_version.h" +#include "wutil.h" // IWYU pragma: keep #define SPACES_PER_INDENT 4 @@ -47,16 +47,12 @@ typedef unsigned int indent_t; static bool dump_parse_tree = false; // Read the entire contents of a file into the specified string. -static wcstring read_file(FILE *f) -{ +static wcstring read_file(FILE *f) { wcstring result; - while (1) - { + while (1) { wint_t c = fgetwc(f); - if (c == WEOF) - { - if (ferror(f)) - { + if (c == WEOF) { + if (ferror(f)) { wperror(L"fgetwc"); exit(1); } @@ -70,64 +66,56 @@ static wcstring read_file(FILE *f) // Append whitespace as necessary. If we have a newline, append the appropriate indent. Otherwise, // append a space. static void append_whitespace(indent_t node_indent, bool do_indent, bool has_new_line, - wcstring *out_result) -{ - if (!has_new_line) - { + wcstring *out_result) { + if (!has_new_line) { out_result->push_back(L' '); - } - else if (do_indent) - { + } else if (do_indent) { out_result->append(node_indent * SPACES_PER_INDENT, L' '); } } // Dump a parse tree node in a form helpful to someone debugging the behavior of this program. -static void dump_node(indent_t node_indent, const parse_node_t &node, const wcstring &source) -{ +static void dump_node(indent_t node_indent, const parse_node_t &node, const wcstring &source) { int nextc_idx = node.source_start + node.source_length; wchar_t prevc = node.source_start > 0 ? source[node.source_start - 1] : L' '; wchar_t nextc = nextc_idx < source.size() ? source[nextc_idx] : L' '; wchar_t prevc_str[4] = {prevc, 0, 0, 0}; wchar_t nextc_str[4] = {nextc, 0, 0, 0}; - if (prevc < L' ') - { + if (prevc < L' ') { prevc_str[0] = L'\\'; prevc_str[1] = L'c'; prevc_str[2] = prevc + '@'; } - if (nextc < L' ') - { + if (nextc < L' ') { nextc_str[0] = L'\\'; nextc_str[1] = L'c'; nextc_str[2] = nextc + '@'; } fwprintf(stderr, L"{off %4d, len %4d, indent %2u, kw %ls, %ls} [%ls|%ls|%ls]\n", - node.source_start, node.source_length, node_indent, - keyword_description(node.keyword), token_type_description(node.type), - prevc_str, source.substr(node.source_start, node.source_length).c_str(), nextc_str); + node.source_start, node.source_length, node_indent, keyword_description(node.keyword), + token_type_description(node.type), prevc_str, + source.substr(node.source_start, node.source_length).c_str(), nextc_str); } static void prettify_node_recursive(const wcstring &source, const parse_node_tree_t &tree, - node_offset_t node_idx, indent_t node_indent, parse_token_type_t parent_type, - bool *has_new_line, wcstring *out_result, bool do_indent) -{ + node_offset_t node_idx, indent_t node_indent, + parse_token_type_t parent_type, bool *has_new_line, + wcstring *out_result, bool do_indent) { const parse_node_t &node = tree.at(node_idx); const parse_token_type_t node_type = node.type; - const parse_token_type_t prev_node_type = node_idx > 0 ? - tree.at(node_idx - 1).type : token_type_invalid; + const parse_token_type_t prev_node_type = + node_idx > 0 ? tree.at(node_idx - 1).type : token_type_invalid; // Increment the indent if we are either a root job_list, or root case_item_list, or in an if or // while header (#1665). const bool is_root_job_list = node_type == symbol_job_list && parent_type != symbol_job_list; - const bool is_root_case_list = node_type == symbol_case_item_list && - parent_type != symbol_case_item_list; - const bool is_if_while_header = \ - (node_type == symbol_job || node_type == symbol_andor_job_list) && - (parent_type == symbol_if_clause || parent_type == symbol_while_header); + const bool is_root_case_list = + node_type == symbol_case_item_list && parent_type != symbol_case_item_list; + const bool is_if_while_header = + (node_type == symbol_job || node_type == symbol_andor_job_list) && + (parent_type == symbol_if_clause || parent_type == symbol_while_header); - if (is_root_job_list || is_root_case_list || is_if_while_header) - { + if (is_root_job_list || is_root_case_list || is_if_while_header) { node_indent += 1; } @@ -135,35 +123,27 @@ static void prettify_node_recursive(const wcstring &source, const parse_node_tre if (node.has_comments()) // handle comments, which come before the text { - const parse_node_tree_t::parse_node_list_t comment_nodes = ( - tree.comment_nodes_for_node(node)); - for (size_t i = 0; i < comment_nodes.size(); i++) - { + const parse_node_tree_t::parse_node_list_t comment_nodes = + (tree.comment_nodes_for_node(node)); + for (size_t i = 0; i < comment_nodes.size(); i++) { const parse_node_t &comment_node = *comment_nodes.at(i); append_whitespace(node_indent, do_indent, *has_new_line, out_result); out_result->append(source, comment_node.source_start, comment_node.source_length); } } - if (node_type == parse_token_type_end) - { + if (node_type == parse_token_type_end) { out_result->push_back(L'\n'); *has_new_line = true; - } - else if ((node_type >= FIRST_PARSE_TOKEN_TYPE && node_type <= LAST_PARSE_TOKEN_TYPE) || - node_type == parse_special_type_parse_error) - { - if (node.keyword != parse_keyword_none) - { + } else if ((node_type >= FIRST_PARSE_TOKEN_TYPE && node_type <= LAST_PARSE_TOKEN_TYPE) || + node_type == parse_special_type_parse_error) { + if (node.keyword != parse_keyword_none) { append_whitespace(node_indent, do_indent, *has_new_line, out_result); out_result->append(keyword_description(node.keyword)); *has_new_line = false; - } - else if (node.has_source()) - { + } else if (node.has_source()) { // Some type representing a particular token. - if (prev_node_type != parse_token_type_redirection) - { + if (prev_node_type != parse_token_type_redirection) { append_whitespace(node_indent, do_indent, *has_new_line, out_result); } out_result->append(source, node.source_start, node.source_length); @@ -172,51 +152,50 @@ static void prettify_node_recursive(const wcstring &source, const parse_node_tre } // Recurse to all our children. - for (node_offset_t idx = 0; idx < node.child_count; idx++) - { + for (node_offset_t idx = 0; idx < node.child_count; idx++) { // Note we pass our type to our child, which becomes its parent node type. prettify_node_recursive(source, tree, node.child_start + idx, node_indent, node_type, - has_new_line, out_result, do_indent); + has_new_line, out_result, do_indent); } } -/* Entry point for prettification. */ -static wcstring prettify(const wcstring &src, bool do_indent) -{ +// Entry point for prettification. +static wcstring prettify(const wcstring &src, bool do_indent) { parse_node_tree_t tree; - if (! parse_tree_from_string(src, parse_flag_continue_after_error | parse_flag_include_comments | parse_flag_leave_unterminated | parse_flag_show_blank_lines, &tree, NULL /* errors */)) - { - /* We return the initial string on failure */ + if (!parse_tree_from_string(src, + parse_flag_continue_after_error | parse_flag_include_comments | + parse_flag_leave_unterminated | parse_flag_show_blank_lines, + &tree, NULL /* errors */)) { + // We return the initial string on failure. return src; } - /* We may have a forest of disconnected trees on a parse failure. We have to handle all nodes that have no parent, and all parse errors. */ + // We may have a forest of disconnected trees on a parse failure. We have to handle all nodes + // that have no parent, and all parse errors. bool has_new_line = true; wcstring result; - for (node_offset_t i=0; i < tree.size(); i++) - { + for (node_offset_t i = 0; i < tree.size(); i++) { const parse_node_t &node = tree.at(i); - if (node.parent == NODE_OFFSET_INVALID || node.type == parse_special_type_parse_error) - { - /* A root node */ - prettify_node_recursive(src, tree, i, 0, symbol_job_list, &has_new_line, &result, do_indent); + if (node.parent == NODE_OFFSET_INVALID || node.type == parse_special_type_parse_error) { + // A root node. + prettify_node_recursive(src, tree, i, 0, symbol_job_list, &has_new_line, &result, + do_indent); } } return result; } - // Helper for output_set_writer static std::string output_receiver; -static int write_to_output_receiver(char c) -{ +static int write_to_output_receiver(char c) { output_receiver.push_back(c); return 0; } -/* Given a string and list of colors of the same size, return the string with ANSI escape sequences representing the colors. */ -static std::string ansi_colorize(const wcstring &text, const std::vector &colors) -{ +/// Given a string and list of colors of the same size, return the string with ANSI escape sequences +/// representing the colors. +static std::string ansi_colorize(const wcstring &text, + const std::vector &colors) { assert(colors.size() == text.size()); assert(output_receiver.empty()); @@ -224,11 +203,9 @@ static std::string ansi_colorize(const wcstring &text, const std::vector &colors) -{ - if (text.empty()) - { +static std::string html_colorize(const wcstring &text, + const std::vector &colors) { + if (text.empty()) { return ""; } assert(colors.size() == text.size()); wcstring html = L"
    ";
         highlight_spec_t last_color = highlight_spec_normal;
    -    for (size_t i=0; i < text.size(); i++)
    -    {
    -        /* Handle colors */
    +    for (size_t i = 0; i < text.size(); i++) {
    +        // Handle colors.
             highlight_spec_t color = colors.at(i);
    -        if (i > 0 && color != last_color)
    -        {
    +        if (i > 0 && color != last_color) {
                 html.append(L"");
             }
    -        if (i == 0 || color != last_color)
    -        {
    +        if (i == 0 || color != last_color) {
                 append_format(html, L"", html_class_name_for_color(color));
             }
             last_color = color;
     
    -        /* Handle text */
    +        // Handle text.
             wchar_t wc = text.at(i);
    -        switch (wc)
    -        {
    -            case L'&':
    +        switch (wc) {
    +            case L'&': {
                     html.append(L"&");
                     break;
    -            case L'\'':
    +            }
    +            case L'\'': {
                     html.append(L"'");
                     break;
    -            case L'"':
    +            }
    +            case L'"': {
                     html.append(L""");
                     break;
    -            case L'<':
    +            }
    +            case L'<': {
                     html.append(L"<");
                     break;
    -            case L'>':
    +            }
    +            case L'>': {
                     html.append(L">");
                     break;
    -            default:
    +            }
    +            default: {
                     html.push_back(wc);
                     break;
    +            }
             }
         }
         html.append(L"
    "); return wcs2string(html); } -static std::string no_colorize(const wcstring &text) -{ - return wcs2string(text); -} +static std::string no_colorize(const wcstring &text) { return wcs2string(text); } -int main(int argc, char *argv[]) -{ +int main(int argc, char *argv[]) { set_main_thread(); setup_fork_guards(); wsetlocale(LC_ALL, L""); - program_name=L"fish_indent"; + program_name = L"fish_indent"; env_init(); input_init(); - /* Types of output we support */ - enum - { + // Types of output we support. + enum { output_type_plain_text, output_type_ansi, output_type_html @@ -344,68 +343,48 @@ int main(int argc, char *argv[]) bool do_indent = true; const char *short_opts = "+dhvi"; - const struct option long_opts[] = - { - { "dump", no_argument, NULL, 'd' }, - { "no-indent", no_argument, NULL, 'i' }, - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'v' }, - { "html", no_argument, NULL, 1 }, - { "ansi", no_argument, NULL, 2 }, - { NULL, 0, NULL, 0 } - }; + const struct option long_opts[] = {{"dump", no_argument, NULL, 'd'}, + {"no-indent", no_argument, NULL, 'i'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'v'}, + {"html", no_argument, NULL, 1}, + {"ansi", no_argument, NULL, 2}, + {NULL, 0, NULL, 0}}; int opt; - while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) - { - switch (opt) - { - case 0: - { + while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) { + switch (opt) { + case 0: { fwprintf(stderr, _(L"getopt_long() unexpectedly returned zero\n")); exit_without_destructors(127); } - - case 'd': - { + case 'd': { dump_parse_tree = true; break; } - - case 'h': - { + case 'h': { print_help("fish_indent", 1); exit_without_destructors(0); } - - case 'v': - { + case 'v': { fwprintf(stderr, _(L"%ls, version %s\n"), program_name, get_fish_version()); exit(0); assert(0 && "Unreachable code reached"); break; } - - case 'i': - { + case 'i': { do_indent = false; break; } - - case 1: - { + case 1: { output_type = output_type_html; break; } - - case 2: - { + case 2: { output_type = output_type_ansi; break; } - - default: - { + default: { // We assume getopt_long() has already emitted a diagnostic msg. exit_without_destructors(1); } @@ -415,27 +394,27 @@ int main(int argc, char *argv[]) const wcstring src = read_file(stdin); const wcstring output_wtext = prettify(src, do_indent); - /* Maybe colorize */ + // Maybe colorize. std::vector colors; - if (output_type != output_type_plain_text) - { - highlight_shell_no_io(output_wtext, colors, output_wtext.size(), NULL, env_vars_snapshot_t::current()); + if (output_type != output_type_plain_text) { + highlight_shell_no_io(output_wtext, colors, output_wtext.size(), NULL, + env_vars_snapshot_t::current()); } std::string colored_output; - switch (output_type) - { - case output_type_plain_text: + switch (output_type) { + case output_type_plain_text: { colored_output = no_colorize(output_wtext); break; - - case output_type_ansi: + } + case output_type_ansi: { colored_output = ansi_colorize(output_wtext, colors); break; - - case output_type_html: + } + case output_type_html: { colored_output = html_colorize(output_wtext, colors); break; + } } fputs(colored_output.c_str(), stdout); diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index b616ec518..b3a309786 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -1,126 +1,104 @@ -/** \file fish_tests.c - Various bug and feature tests. Compiled and run by make test. -*/ +// Various bug and feature tests. Compiled and run by make test. // IWYU pragma: no_include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include +#include #include -#include +#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include #include +#include +#include #include -#include -#include -#include -#include "fallback.h" // IWYU pragma: keep -#include "util.h" -#include "common.h" -#include "proc.h" -#include "reader.h" #include "builtin.h" -#include "function.h" +#include "color.h" +#include "common.h" #include "complete.h" -#include "wutil.h" // IWYU pragma: keep #include "env.h" -#include "expand.h" -#include "parser.h" -#include "tokenizer.h" +#include "env_universal_common.h" #include "event.h" -#include "path.h" -#include "history.h" +#include "expand.h" +#include "fallback.h" // IWYU pragma: keep +#include "function.h" #include "highlight.h" +#include "history.h" +#include "input.h" +#include "input_common.h" +#include "io.h" #include "iothread.h" -#include "signal.h" +#include "lru.h" +#include "pager.h" +#include "parse_constants.h" #include "parse_tree.h" #include "parse_util.h" -#include "pager.h" -#include "input.h" -#include "wildcard.h" -#include "utf8.h" -#include "env_universal_common.h" -#include "wcstringutil.h" -#include "color.h" -#include "io.h" -#include "lru.h" -#include "parse_constants.h" +#include "parser.h" +#include "path.h" +#include "proc.h" +#include "reader.h" #include "screen.h" -#include "input_common.h" +#include "signal.h" +#include "tokenizer.h" +#include "utf8.h" +#include "util.h" +#include "wcstringutil.h" +#include "wildcard.h" +#include "wutil.h" // IWYU pragma: keep -static const char * const * s_arguments; +static const char *const *s_arguments; static int s_test_run_count = 0; -/* Indicate if we should test the given function. Either we test everything (all arguments) or we run only tests that have a prefix in s_arguments */ -static bool should_test_function(const char *func_name) -{ - /* No args, test everything */ +// Indicate if we should test the given function. Either we test everything (all arguments) or we +// run only tests that have a prefix in s_arguments. +static bool should_test_function(const char *func_name) { + // No args, test everything. bool result = false; - if (! s_arguments || ! s_arguments[0]) - { + if (!s_arguments || !s_arguments[0]) { result = true; - } - else - { - for (size_t i=0; s_arguments[i] != NULL; i++) - { - if (! strncmp(func_name, s_arguments[i], strlen(s_arguments[i]))) - { - /* Prefix match */ + } else { + for (size_t i = 0; s_arguments[i] != NULL; i++) { + if (!strncmp(func_name, s_arguments[i], strlen(s_arguments[i]))) { + // Prefix match. result = true; break; } } } - if (result) - s_test_run_count++; + if (result) s_test_run_count++; return result; } -/** - The number of tests to run - */ +/// The number of tests to run. #define ESCAPE_TEST_COUNT 100000 -/** - The average length of strings to unescape - */ +/// The average length of strings to unescape. #define ESCAPE_TEST_LENGTH 100 -/** - The higest character number of character to try and escape - */ +/// The higest character number of character to try and escape. #define ESCAPE_TEST_CHAR 4000 - -/** - Number of laps to run performance testing loop -*/ +/// Number of laps to run performance testing loop. #define LAPS 50 -/** - Number of encountered errors -*/ -static int err_count=0; +/// Number of encountered errors. +static int err_count = 0; -/** - Print formatted output -*/ -static void say(const wchar_t *blah, ...) -{ +/// Print formatted output. +static void say(const wchar_t *blah, ...) { va_list va; va_start(va, blah); vwprintf(blah, va); @@ -128,45 +106,36 @@ static void say(const wchar_t *blah, ...) wprintf(L"\n"); } -/** - Print formatted error string -*/ -static void err(const wchar_t *blah, ...) -{ +/// Print formatted error string. +static void err(const wchar_t *blah, ...) { va_list va; va_start(va, blah); err_count++; - // Xcode's term doesn't support color (even though TERM claims it does) - bool colorize = ! getenv("RUNNING_IN_XCODE"); + // Xcode's term doesn't support color (even though TERM claims it does). + bool colorize = !getenv("RUNNING_IN_XCODE"); - // show errors in red - if (colorize) - { + // Show errors in red. + if (colorize) { fputs("\x1b[31m", stdout); } - wprintf(L"Error: "); vwprintf(blah, va); va_end(va); - // return to normal color - if (colorize) - { + // Return to normal color. + if (colorize) { fputs("\x1b[0m", stdout); } wprintf(L"\n"); } -// Joins a wcstring_list_t via commas -static wcstring comma_join(const wcstring_list_t &lst) -{ +/// Joins a wcstring_list_t via commas. +static wcstring comma_join(const wcstring_list_t &lst) { wcstring result; - for (size_t i=0; i < lst.size(); i++) - { - if (i > 0) - { + for (size_t i = 0; i < lst.size(); i++) { + if (i > 0) { result.push_back(L','); } result.append(lst.at(i)); @@ -174,206 +143,162 @@ static wcstring comma_join(const wcstring_list_t &lst) return result; } -/* Helper to chdir and then update $PWD */ -static int chdir_set_pwd(const char *path) -{ +/// Helper to chdir and then update $PWD. +static int chdir_set_pwd(const char *path) { int ret = chdir(path); - if (ret == 0) - { + if (ret == 0) { env_set_pwd(); } return ret; } -#define do_test(e) do { if (! (e)) err(L"Test failed on line %lu: %s", __LINE__, #e); } while (0) +#define do_test(e) \ + do { \ + if (!(e)) err(L"Test failed on line %lu: %s", __LINE__, #e); \ + } while (0) -#define do_test1(e, msg) do { if (! (e)) err(L"Test failed on line %lu: %ls", __LINE__, (msg)); } while (0) +#define do_test1(e, msg) \ + do { \ + if (!(e)) err(L"Test failed on line %lu: %ls", __LINE__, (msg)); \ + } while (0) -/* Test sane escapes */ -static void test_unescape_sane() -{ - const struct test_t - { - const wchar_t * input; - const wchar_t * expected; - } tests[] = - { - {L"abcd", L"abcd"}, - {L"'abcd'", L"abcd"}, - {L"'abcd\\n'", L"abcd\\n"}, - {L"\"abcd\\n\"", L"abcd\\n"}, - {L"\"abcd\\n\"", L"abcd\\n"}, - {L"\\143", L"c"}, - {L"'\\143'", L"\\143"}, - {L"\\n", L"\n"} // \n normally becomes newline +/// Test sane escapes. +static void test_unescape_sane() { + const struct test_t { + const wchar_t *input; + const wchar_t *expected; + } tests[] = { + {L"abcd", L"abcd"}, {L"'abcd'", L"abcd"}, + {L"'abcd\\n'", L"abcd\\n"}, {L"\"abcd\\n\"", L"abcd\\n"}, + {L"\"abcd\\n\"", L"abcd\\n"}, {L"\\143", L"c"}, + {L"'\\143'", L"\\143"}, {L"\\n", L"\n"} // \n normally becomes newline }; wcstring output; - for (size_t i=0; i < sizeof tests / sizeof *tests; i++) - { + for (size_t i = 0; i < sizeof tests / sizeof *tests; i++) { bool ret = unescape_string(tests[i].input, &output, UNESCAPE_DEFAULT); - if (! ret) - { + if (!ret) { err(L"Failed to unescape '%ls'\n", tests[i].input); - } - else if (output != tests[i].expected) - { - err(L"In unescaping '%ls', expected '%ls' but got '%ls'\n", tests[i].input, tests[i].expected, output.c_str()); + } else if (output != tests[i].expected) { + err(L"In unescaping '%ls', expected '%ls' but got '%ls'\n", tests[i].input, + tests[i].expected, output.c_str()); } } - // test for overflow - if (unescape_string(L"echo \\UFFFFFF", &output, UNESCAPE_DEFAULT)) - { + // Test for overflow. + if (unescape_string(L"echo \\UFFFFFF", &output, UNESCAPE_DEFAULT)) { err(L"Should not have been able to unescape \\UFFFFFF\n"); } - if (unescape_string(L"echo \\U110000", &output, UNESCAPE_DEFAULT)) - { + if (unescape_string(L"echo \\U110000", &output, UNESCAPE_DEFAULT)) { err(L"Should not have been able to unescape \\U110000\n"); } - if (! unescape_string(L"echo \\U10FFFF", &output, UNESCAPE_DEFAULT)) - { + if (!unescape_string(L"echo \\U10FFFF", &output, UNESCAPE_DEFAULT)) { err(L"Should have been able to unescape \\U10FFFF\n"); } - - } -/** - Test the escaping/unescaping code by escaping/unescaping random - strings and verifying that the original string comes back. -*/ - -static void test_escape_crazy() -{ +/// Test the escaping/unescaping code by escaping/unescaping random strings and verifying that the +/// original string comes back. +static void test_escape_crazy() { say(L"Testing escaping and unescaping"); wcstring random_string; wcstring escaped_string; wcstring unescaped_string; - for (size_t i=0; i", escaped_string.c_str()); - } - else if (unescaped_string != random_string) - { - err(L"Escaped and then unescaped string '%ls', but got back a different string '%ls'", random_string.c_str(), unescaped_string.c_str()); + } else if (unescaped_string != random_string) { + err(L"Escaped and then unescaped string '%ls', but got back a different string '%ls'", + random_string.c_str(), unescaped_string.c_str()); } } } -static void test_format(void) -{ +static void test_format(void) { say(L"Testing formatting functions"); - struct - { + struct { unsigned long long val; const char *expected; - } tests[] = - { - { 0, "empty" }, - { 1, "1B" }, - { 2, "2B" }, - { 1024, "1kB" }, - { 1870, "1.8kB" }, - { 4322911, "4.1MB" } - }; + } tests[] = {{0, "empty"}, {1, "1B"}, {2, "2B"}, + {1024, "1kB"}, {1870, "1.8kB"}, {4322911, "4.1MB"}}; size_t i; - for (i=0; i < sizeof tests / sizeof *tests; i++) - { + for (i = 0; i < sizeof tests / sizeof *tests; i++) { char buff[128]; format_size_safe(buff, tests[i].val); - do_test(! strcmp(buff, tests[i].expected)); + do_test(!strcmp(buff, tests[i].expected)); } - for (int j=-129; j <= 129; j++) - { + for (int j = -129; j <= 129; j++) { char buff1[128], buff2[128]; format_long_safe(buff1, j); sprintf(buff2, "%d", j); - do_test(! strcmp(buff1, buff2)); + do_test(!strcmp(buff1, buff2)); wchar_t wbuf1[128], wbuf2[128]; format_long_safe(wbuf1, j); swprintf(wbuf2, 128, L"%d", j); - do_test(! wcscmp(wbuf1, wbuf2)); - + do_test(!wcscmp(wbuf1, wbuf2)); } long q = LONG_MIN; char buff1[128], buff2[128]; format_long_safe(buff1, q); sprintf(buff2, "%ld", q); - do_test(! strcmp(buff1, buff2)); + do_test(!strcmp(buff1, buff2)); } -/** - Test wide/narrow conversion by creating random strings and - verifying that the original string comes back thorugh double - conversion. -*/ -static void test_convert() -{ - /* char o[] = - { - -17, -128, -121, -68, 0 - } - ; +/// Test wide/narrow conversion by creating random strings and verifying that the original string +/// comes back thorugh double conversion. +static void test_convert() { +#if 0 + char o[] = {-17, -128, -121, -68, 0}; - wchar_t *w = str2wcs(o); - char *n = wcs2str(w); + wchar_t *w = str2wcs(o); + char *n = wcs2str(w); + int i; - int i; + for (i = 0; o[i]; i++) { + bitprint(o[i]); + // wprintf(L"%d ", o[i]); + } + wprintf(L"\n"); - for( i=0; o[i]; i++ ) - { - bitprint(o[i]);; - //wprintf(L"%d ", o[i]); - } - wprintf(L"\n"); + for (i = 0; w[i]; i++) { + wbitprint(w[i]); + // wprintf(L"%d ", w[i]); + } + wprintf(L"\n"); - for( i=0; w[i]; i++ ) - { - wbitprint(w[i]);; - //wprintf(L"%d ", w[i]); - } - wprintf(L"\n"); - - for( i=0; n[i]; i++ ) - { - bitprint(n[i]);; - //wprintf(L"%d ", n[i]); - } - wprintf(L"\n"); - - return; - */ + for (i = 0; n[i]; i++) { + bitprint(n[i]); + // wprintf(L"%d ", n[i]); + } + wprintf(L"\n"); + return; +#endif int i; std::vector sb; say(L"Testing wide/narrow string conversion"); - for (i=0; i&1 'nested \"quoted\" '(string containing subshells " + L"){and,brackets}$as[$well (as variable arrays)] not_a_redirect^ ^ ^^is_a_redirect " + L"Compress_Newlines\n \n\t\n \nInto_Just_One"; + 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_STRING, TOK_END, + TOK_STRING}; + + say(L"Test correct tokenization"); + { - - 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 Compress_Newlines\n \n\t\n \nInto_Just_One"; - 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_STRING, TOK_END, TOK_STRING - }; - - say(L"Test correct tokenization"); - tokenizer_t t(str, 0); - tok_t token; size_t i = 0; - while (t.next(&token)) - { - if (i > sizeof types / sizeof *types) - { + while (t.next(&token)) { + if (i > sizeof types / sizeof *types) { err(L"Too many tokens returned from tokenizer"); break; } - if (types[i] != token.type) - { + if (types[i] != token.type) { err(L"Tokenization error:"); - wprintf(L"Token number %zu of string \n'%ls'\n, got token type %ld\n", - i + 1, str, (long)token.type); + wprintf(L"Token number %zu of string \n'%ls'\n, got token type %ld\n", i + 1, str, + (long)token.type); } i++; } - if (i < sizeof types / sizeof *types) - { + if (i < sizeof types / sizeof *types) { err(L"Too few tokens returned from tokenizer"); } } - /* Test some errors */ + // Test some errors. { - tok_t token; tokenizer_t t(L"abc\\", 0); do_test(t.next(&token)); do_test(token.type == TOK_ERROR); @@ -484,7 +395,6 @@ static void test_tok() } { - tok_t token; tokenizer_t t(L"abc defg(hij (klm)", 0); do_test(t.next(&token)); do_test(t.next(&token)); @@ -494,7 +404,6 @@ static void test_tok() } { - tok_t token; tokenizer_t t(L"abc defg[hij (klm)", 0); do_test(t.next(&token)); do_test(t.next(&token)); @@ -503,259 +412,230 @@ static void test_tok() do_test(token.error_offset == 4); } - /* Test redirection_type_for_string */ - if (redirection_type_for_string(L"<") != TOK_REDIRECT_IN) err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); - if (redirection_type_for_string(L"^") != TOK_REDIRECT_OUT) err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); - if (redirection_type_for_string(L">") != TOK_REDIRECT_OUT) err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); - if (redirection_type_for_string(L"2>") != TOK_REDIRECT_OUT) err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); - if (redirection_type_for_string(L">>") != TOK_REDIRECT_APPEND) err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); - if (redirection_type_for_string(L"2>>") != TOK_REDIRECT_APPEND) err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); - if (redirection_type_for_string(L"2>?") != TOK_REDIRECT_NOCLOB) err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); - if (redirection_type_for_string(L"9999999999999999>?") != TOK_NONE) err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); - if (redirection_type_for_string(L"2>&3") != TOK_REDIRECT_FD) err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); - if (redirection_type_for_string(L"2>|") != TOK_NONE) err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); + // Test redirection_type_for_string. + if (redirection_type_for_string(L"<") != TOK_REDIRECT_IN) + err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); + if (redirection_type_for_string(L"^") != TOK_REDIRECT_OUT) + err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); + if (redirection_type_for_string(L">") != TOK_REDIRECT_OUT) + err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); + if (redirection_type_for_string(L"2>") != TOK_REDIRECT_OUT) + err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); + if (redirection_type_for_string(L">>") != TOK_REDIRECT_APPEND) + err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); + if (redirection_type_for_string(L"2>>") != TOK_REDIRECT_APPEND) + err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); + if (redirection_type_for_string(L"2>?") != TOK_REDIRECT_NOCLOB) + err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); + if (redirection_type_for_string(L"9999999999999999>?") != TOK_NONE) + err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); + if (redirection_type_for_string(L"2>&3") != TOK_REDIRECT_FD) + err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); + if (redirection_type_for_string(L"2>|") != TOK_NONE) + err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); } -// Little function that runs in the main thread -static int test_iothread_main_call(int *addr) -{ +// Little function that runs in the main thread. +static int test_iothread_main_call(int *addr) { *addr += 1; return *addr; } -// Little function that runs in a background thread, bouncing to the main -static int test_iothread_thread_call(int *addr) -{ +// Little function that runs in a background thread, bouncing to the main. +static int test_iothread_thread_call(int *addr) { int before = *addr; iothread_perform_on_main(test_iothread_main_call, addr); int after = *addr; - // Must have incremented it at least once - if (before >= after) - { + // Must have incremented it at least once. + if (before >= after) { err(L"Failed to increment from background thread"); } return after; } -static void test_iothread(void) -{ +static void test_iothread(void) { say(L"Testing iothreads"); int *int_ptr = new int(0); int iterations = 50000; int max_achieved_thread_count = 0; double start = timef(); - for (int i=0; i < iterations; i++) - { + for (int i = 0; i < iterations; i++) { int thread_count = iothread_perform(test_iothread_thread_call, int_ptr); max_achieved_thread_count = std::max(max_achieved_thread_count, thread_count); } - // Now wait until we're done + // Now wait until we're done. iothread_drain_all(); double end = timef(); - // Should have incremented it once per thread - if (*int_ptr != iterations) - { + // Should have incremented it once per thread. + if (*int_ptr != iterations) { say(L"Expected int to be %d, but instead it was %d", iterations, *int_ptr); } - say(L" (%.02f msec, with max of %d threads)", (end - start) * 1000.0, max_achieved_thread_count); + say(L" (%.02f msec, with max of %d threads)", (end - start) * 1000.0, + max_achieved_thread_count); delete int_ptr; } -static parser_test_error_bits_t detect_argument_errors(const wcstring &src) -{ +static parser_test_error_bits_t detect_argument_errors(const wcstring &src) { parse_node_tree_t tree; - if (! parse_tree_from_string(src, parse_flag_none, &tree, NULL, symbol_argument_list)) - { + if (!parse_tree_from_string(src, parse_flag_none, &tree, NULL, symbol_argument_list)) { return PARSER_TEST_ERROR; } - assert(! tree.empty()); + assert(!tree.empty()); const parse_node_t *first_arg = tree.next_node_in_node_list(tree.at(0), symbol_argument, NULL); assert(first_arg != NULL); return parse_util_detect_errors_in_argument(*first_arg, first_arg->get_source(src)); } -/** - Test the parser -*/ -static void test_parser() -{ +/// Test the parser. +static void test_parser() { say(L"Testing parser"); parser_t parser; say(L"Testing block nesting"); - if (!parse_util_detect_errors(L"if; end")) - { + if (!parse_util_detect_errors(L"if; end")) { err(L"Incomplete if statement undetected"); } - if (!parse_util_detect_errors(L"if test; echo")) - { + if (!parse_util_detect_errors(L"if test; echo")) { err(L"Missing end undetected"); } - if (!parse_util_detect_errors(L"if test; end; end")) - { + if (!parse_util_detect_errors(L"if test; end; end")) { err(L"Unbalanced end undetected"); } say(L"Testing detection of invalid use of builtin commands"); - if (!parse_util_detect_errors(L"case foo")) - { + if (!parse_util_detect_errors(L"case foo")) { err(L"'case' command outside of block context undetected"); } - if (!parse_util_detect_errors(L"switch ggg; if true; case foo;end;end")) - { + if (!parse_util_detect_errors(L"switch ggg; if true; case foo;end;end")) { err(L"'case' command outside of switch block context undetected"); } - if (!parse_util_detect_errors(L"else")) - { + if (!parse_util_detect_errors(L"else")) { err(L"'else' command outside of conditional block context undetected"); } - if (!parse_util_detect_errors(L"else if")) - { + if (!parse_util_detect_errors(L"else if")) { err(L"'else if' command outside of conditional block context undetected"); } - if (!parse_util_detect_errors(L"if false; else if; end")) - { + if (!parse_util_detect_errors(L"if false; else if; end")) { err(L"'else if' missing command undetected"); } - if (!parse_util_detect_errors(L"break")) - { + if (!parse_util_detect_errors(L"break")) { err(L"'break' command outside of loop block context undetected"); } - if (parse_util_detect_errors(L"break --help")) - { + if (parse_util_detect_errors(L"break --help")) { err(L"'break --help' incorrectly marked as error"); } - if (! parse_util_detect_errors(L"while false ; function foo ; break ; end ; end ")) - { + if (!parse_util_detect_errors(L"while false ; function foo ; break ; end ; end ")) { err(L"'break' command inside function allowed to break from loop outside it"); } - - if (!parse_util_detect_errors(L"exec ls|less") || !parse_util_detect_errors(L"echo|return")) - { + if (!parse_util_detect_errors(L"exec ls|less") || !parse_util_detect_errors(L"echo|return")) { err(L"Invalid pipe command undetected"); } - if (parse_util_detect_errors(L"for i in foo ; switch $i ; case blah ; break; end; end ")) - { + if (parse_util_detect_errors(L"for i in foo ; switch $i ; case blah ; break; end; end ")) { err(L"'break' command inside switch falsely reported as error"); } - if (parse_util_detect_errors(L"or cat | cat") || parse_util_detect_errors(L"and cat | cat")) - { + if (parse_util_detect_errors(L"or cat | cat") || parse_util_detect_errors(L"and cat | cat")) { err(L"boolean command at beginning of pipeline falsely reported as error"); } - if (! parse_util_detect_errors(L"cat | and cat")) - { + if (!parse_util_detect_errors(L"cat | and cat")) { err(L"'and' command in pipeline not reported as error"); } - if (! parse_util_detect_errors(L"cat | or cat")) - { + if (!parse_util_detect_errors(L"cat | or cat")) { err(L"'or' command in pipeline not reported as error"); } - if (! parse_util_detect_errors(L"cat | exec") || ! parse_util_detect_errors(L"exec | cat")) - { + if (!parse_util_detect_errors(L"cat | exec") || !parse_util_detect_errors(L"exec | cat")) { err(L"'exec' command in pipeline not reported as error"); } - if (detect_argument_errors(L"foo")) - { + if (detect_argument_errors(L"foo")) { err(L"simple argument reported as error"); } - if (detect_argument_errors(L"''")) - { + if (detect_argument_errors(L"''")) { err(L"Empty string reported as error"); } - - if (!(detect_argument_errors(L"foo$$") & PARSER_TEST_ERROR)) - { + if (!(detect_argument_errors(L"foo$$") & PARSER_TEST_ERROR)) { err(L"Bad variable expansion not reported as error"); } - if (!(detect_argument_errors(L"foo$@") & PARSER_TEST_ERROR)) - { + if (!(detect_argument_errors(L"foo$@") & PARSER_TEST_ERROR)) { err(L"Bad variable expansion not reported as error"); } - /* Within command substitutions, we should be able to detect everything that parse_util_detect_errors can detect */ - if (!(detect_argument_errors(L"foo(cat | or cat)") & PARSER_TEST_ERROR)) - { + // Within command substitutions, we should be able to detect everything that + // parse_util_detect_errors can detect. + if (!(detect_argument_errors(L"foo(cat | or cat)") & PARSER_TEST_ERROR)) { err(L"Bad command substitution not reported as error"); } - if (!(detect_argument_errors(L"foo\\xFF9") & PARSER_TEST_ERROR)) - { + if (!(detect_argument_errors(L"foo\\xFF9") & PARSER_TEST_ERROR)) { err(L"Bad escape not reported as error"); } - if (!(detect_argument_errors(L"foo(echo \\xFF9)") & PARSER_TEST_ERROR)) - { + if (!(detect_argument_errors(L"foo(echo \\xFF9)") & PARSER_TEST_ERROR)) { err(L"Bad escape in command substitution not reported as error"); } - if (!(detect_argument_errors(L"foo(echo (echo (echo \\xFF9)))") & PARSER_TEST_ERROR)) - { + if (!(detect_argument_errors(L"foo(echo (echo (echo \\xFF9)))") & PARSER_TEST_ERROR)) { err(L"Bad escape in nested command substitution not reported as error"); } - if (! parse_util_detect_errors(L"false & ; and cat")) - { + if (!parse_util_detect_errors(L"false & ; and cat")) { err(L"'and' command after background not reported as error"); } - if (! parse_util_detect_errors(L"true & ; or cat")) - { + if (!parse_util_detect_errors(L"true & ; or cat")) { err(L"'or' command after background not reported as error"); } - if (parse_util_detect_errors(L"true & ; not cat")) - { + if (parse_util_detect_errors(L"true & ; not cat")) { err(L"'not' command after background falsely reported as error"); } - - if (! parse_util_detect_errors(L"if true & ; end")) - { + if (!parse_util_detect_errors(L"if true & ; end")) { err(L"backgrounded 'if' conditional not reported as error"); } - if (! parse_util_detect_errors(L"if false; else if true & ; end")) - { + if (!parse_util_detect_errors(L"if false; else if true & ; end")) { err(L"backgrounded 'else if' conditional not reported as error"); } - if (! parse_util_detect_errors(L"while true & ; end")) - { + if (!parse_util_detect_errors(L"while true & ; end")) { err(L"backgrounded 'while' conditional not reported as error"); } say(L"Testing basic evaluation"); - /* Ensure that we don't crash on infinite self recursion and mutual recursion. These must use the principal parser because we cannot yet execute jobs on other parsers (!) */ + // Ensure that we don't crash on infinite self recursion and mutual recursion. These must use + // the principal parser because we cannot yet execute jobs on other parsers. say(L"Testing recursion detection"); - parser_t::principal_parser().eval(L"function recursive ; recursive ; end ; recursive; ", io_chain_t(), TOP); + parser_t::principal_parser().eval(L"function recursive ; recursive ; end ; recursive; ", + io_chain_t(), TOP); #if 0 - /* This is disabled since it produces a long backtrace. We should find a way to either visually compress the backtrace, or disable error spewing */ + // This is disabled since it produces a long backtrace. We should find a way to either visually + // compress the backtrace, or disable error spewing. parser_t::principal_parser().eval(L"function recursive1 ; recursive2 ; end ; function recursive2 ; recursive1 ; end ; recursive1; ", io_chain_t(), TOP); #endif say(L"Testing empty function name"); - parser_t::principal_parser().eval(L"function '' ; echo fail; exit 42 ; end ; ''", io_chain_t(), TOP); + parser_t::principal_parser().eval(L"function '' ; echo fail; exit 42 ; end ; ''", io_chain_t(), + TOP); say(L"Testing eval_args"); completion_list_t comps; @@ -766,55 +646,54 @@ static void test_parser() do_test(comps.at(2).completion == L"delta"); } -/* Wait a while and then SIGINT the main thread */ -struct test_cancellation_info_t -{ +/// Wait a while and then SIGINT the main thread. +struct test_cancellation_info_t { pthread_t thread; double delay; }; -static int signal_main(test_cancellation_info_t *info) -{ +static int signal_main(test_cancellation_info_t *info) { usleep(info->delay * 1E6); pthread_kill(info->thread, SIGINT); return 0; } -static void test_1_cancellation(const wchar_t *src) -{ +static void test_1_cancellation(const wchar_t *src) { shared_ptr out_buff(io_buffer_t::create(STDOUT_FILENO, io_chain_t())); const io_chain_t io_chain(out_buff); - test_cancellation_info_t ctx = {pthread_self(), 0.25 /* seconds */ }; + test_cancellation_info_t ctx = {pthread_self(), 0.25 /* seconds */}; iothread_perform(signal_main, &ctx); parser_t::principal_parser().eval(src, io_chain, TOP); out_buff->read(); - if (out_buff->out_buffer_size() != 0) - { - err(L"Expected 0 bytes in out_buff, but instead found %lu bytes\n", out_buff->out_buffer_size()); + if (out_buff->out_buffer_size() != 0) { + err(L"Expected 0 bytes in out_buff, but instead found %lu bytes\n", + out_buff->out_buffer_size()); } iothread_drain_all(); } -static void test_cancellation() -{ +static void test_cancellation() { if (getenv("RUNNING_IN_XCODE")) { say(L"Skipping Ctrl-C cancellation test because we are running in Xcode debugger"); return; } say(L"Testing Ctrl-C cancellation. If this hangs, that's a bug!"); - /* Enable fish's signal handling here. We need to make this interactive for fish to install its signal handlers */ + // Enable fish's signal handling here. We need to make this interactive for fish to install its + // signal handlers. proc_push_interactive(1); signal_set_handlers(); - /* This tests that we can correctly ctrl-C out of certain loop constructs, and that nothing gets printed if we do */ + // This tests that we can correctly ctrl-C out of certain loop constructs, and that nothing gets + // printed if we do. - /* Here the command substitution is an infinite loop. echo never even gets its argument, so when we cancel we expect no output */ + // Here the command substitution is an infinite loop. echo never even gets its argument, so when + // we cancel we expect no output. test_1_cancellation(L"echo (while true ; echo blah ; end)"); fprintf(stderr, "."); - /* Nasty infinite loop that doesn't actually execute anything */ + // Nasty infinite loop that doesn't actually execute anything. test_1_cancellation(L"echo (while true ; end) (while true ; end) (while true ; end)"); fprintf(stderr, "."); @@ -826,145 +705,78 @@ static void test_cancellation() fprintf(stderr, "\n"); - /* Restore signal handling */ + // Restore signal handling. proc_pop_interactive(); signal_reset_handlers(); - /* Ensure that we don't think we should cancel */ + // Ensure that we don't think we should cancel. reader_reset_interrupted(); } -static void test_indents() -{ +static void test_indents() { say(L"Testing indents"); - // Here are the components of our source and the indents we expect those to be - struct indent_component_t - { + // Here are the components of our source and the indents we expect those to be. + struct indent_component_t { const wchar_t *txt; int indent; }; - const indent_component_t components1[] = - { - {L"if foo", 0}, - {L"end", 0}, - {NULL, -1} - }; + const indent_component_t components1[] = {{L"if foo", 0}, {L"end", 0}, {NULL, -1}}; - const indent_component_t components2[] = - { - {L"if foo", 0}, - {L"", 1}, //trailing newline! - {NULL, -1} - }; + const indent_component_t components2[] = {{L"if foo", 0}, + {L"", 1}, // trailing newline! + {NULL, -1}}; - const indent_component_t components3[] = - { - {L"if foo", 0}, - {L"foo", 1}, - {L"end", 0}, //trailing newline! - {NULL, -1} - }; + const indent_component_t components3[] = {{L"if foo", 0}, + {L"foo", 1}, + {L"end", 0}, // trailing newline! + {NULL, -1}}; - const indent_component_t components4[] = - { - {L"if foo", 0}, - {L"if bar", 1}, - {L"end", 1}, - {L"end", 0}, - {L"", 0}, - {NULL, -1} - }; + const indent_component_t components4[] = {{L"if foo", 0}, {L"if bar", 1}, {L"end", 1}, + {L"end", 0}, {L"", 0}, {NULL, -1}}; - const indent_component_t components5[] = - { - {L"if foo", 0}, - {L"if bar", 1}, - {L"", 2}, - {NULL, -1} - }; + const indent_component_t components5[] = {{L"if foo", 0}, {L"if bar", 1}, {L"", 2}, {NULL, -1}}; - const indent_component_t components6[] = - { - {L"begin", 0}, - {L"foo", 1}, - {L"", 1}, - {NULL, -1} - }; + const indent_component_t components6[] = {{L"begin", 0}, {L"foo", 1}, {L"", 1}, {NULL, -1}}; - const indent_component_t components7[] = - { - {L"begin", 0}, - {L";", 1}, - {L"end", 0}, - {L"foo", 0}, - {L"", 0}, - {NULL, -1} - }; + const indent_component_t components7[] = {{L"begin", 0}, {L";", 1}, {L"end", 0}, + {L"foo", 0}, {L"", 0}, {NULL, -1}}; - const indent_component_t components8[] = - { - {L"if foo", 0}, - {L"if bar", 1}, - {L"baz", 2}, - {L"end", 1}, - {L"", 1}, - {NULL, -1} - }; + const indent_component_t components8[] = {{L"if foo", 0}, {L"if bar", 1}, {L"baz", 2}, + {L"end", 1}, {L"", 1}, {NULL, -1}}; - const indent_component_t components9[] = - { - {L"switch foo", 0}, - {L"", 1}, - {NULL, -1} - }; + const indent_component_t components9[] = {{L"switch foo", 0}, {L"", 1}, {NULL, -1}}; - const indent_component_t components10[] = - { - {L"switch foo", 0}, - {L"case bar", 1}, - {L"case baz", 1}, - {L"quux", 2}, - {L"", 2}, - {NULL, -1} - }; + const indent_component_t components10[] = { + {L"switch foo", 0}, {L"case bar", 1}, {L"case baz", 1}, {L"quux", 2}, {L"", 2}, {NULL, -1}}; - const indent_component_t components11[] = - { - {L"switch foo", 0}, - {L"cas", 1}, //parse error indentation handling - {NULL, -1} - }; + const indent_component_t components11[] = {{L"switch foo", 0}, + {L"cas", 1}, // parse error indentation handling + {NULL, -1}}; - const indent_component_t components12[] = - { - {L"while false", 0}, - {L"# comment", 1}, //comment indentation handling - {L"command", 1}, //comment indentation handling - {L"# comment2", 1}, //comment indentation handling - {NULL, -1} - }; + const indent_component_t components12[] = {{L"while false", 0}, + {L"# comment", 1}, // comment indentation handling + {L"command", 1}, // comment indentation handling + {L"# comment2", 1}, // comment indentation handling + {NULL, -1}}; - - const indent_component_t *tests[] = {components1, components2, components3, components4, components5, components6, components7, components8, components9, components10, components11, components12}; - for (size_t which = 0; which < sizeof tests / sizeof *tests; which++) - { + const indent_component_t *tests[] = {components1, components2, components3, components4, + components5, components6, components7, components8, + components9, components10, components11, components12}; + for (size_t which = 0; which < sizeof tests / sizeof *tests; which++) { const indent_component_t *components = tests[which]; - // Count how many we have + // Count how many we have. size_t component_count = 0; - while (components[component_count].txt != NULL) - { + while (components[component_count].txt != NULL) { component_count++; } - // Generate the expected indents + // Generate the expected indents. wcstring text; std::vector expected_indents; - for (size_t i=0; i < component_count; i++) - { - if (i > 0) - { + for (size_t i = 0; i < component_count; i++) { + if (i > 0) { text.push_back(L'\n'); expected_indents.push_back(components[i].indent); } @@ -973,116 +785,105 @@ static void test_indents() } do_test(expected_indents.size() == text.size()); - // Compute the indents + // Compute the indents. std::vector indents = parse_util_compute_indents(text); - if (expected_indents.size() != indents.size()) - { - err(L"Indent vector has wrong size! Expected %lu, actual %lu", expected_indents.size(), indents.size()); + if (expected_indents.size() != indents.size()) { + err(L"Indent vector has wrong size! Expected %lu, actual %lu", expected_indents.size(), + indents.size()); } do_test(expected_indents.size() == indents.size()); - for (size_t i=0; i < text.size(); i++) - { - if (expected_indents.at(i) != indents.at(i)) - { - err(L"Wrong indent at index %lu in test #%lu (expected %d, actual %d):\n%ls\n", i, which + 1, expected_indents.at(i), indents.at(i), text.c_str()); - break; //don't keep showing errors for the rest of the line + for (size_t i = 0; i < text.size(); i++) { + if (expected_indents.at(i) != indents.at(i)) { + err(L"Wrong indent at index %lu in test #%lu (expected %d, actual %d):\n%ls\n", i, + which + 1, expected_indents.at(i), indents.at(i), text.c_str()); + break; // don't keep showing errors for the rest of the line } } - } } -static void test_utils() -{ +static void test_utils() { say(L"Testing utils"); const wchar_t *a = L"echo (echo (echo hi"; const wchar_t *begin = NULL, *end = NULL; parse_util_cmdsubst_extent(a, 0, &begin, &end); - if (begin != a || end != begin + wcslen(begin)) err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__); + if (begin != a || end != begin + wcslen(begin)) + err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__); parse_util_cmdsubst_extent(a, 1, &begin, &end); - if (begin != a || end != begin + wcslen(begin)) err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__); + if (begin != a || end != begin + wcslen(begin)) + err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__); parse_util_cmdsubst_extent(a, 2, &begin, &end); - if (begin != a || end != begin + wcslen(begin)) err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__); + if (begin != a || end != begin + wcslen(begin)) + err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__); parse_util_cmdsubst_extent(a, 3, &begin, &end); - if (begin != a || end != begin + wcslen(begin)) err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__); + if (begin != a || end != begin + wcslen(begin)) + err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__); parse_util_cmdsubst_extent(a, 8, &begin, &end); - if (begin != a + wcslen(L"echo (")) err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__); + if (begin != a + wcslen(L"echo (")) + err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__); parse_util_cmdsubst_extent(a, 17, &begin, &end); - if (begin != a + wcslen(L"echo (echo (")) err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__); + if (begin != a + wcslen(L"echo (echo (")) + err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__); } -/* UTF8 tests taken from Alexey Vatchenko's utf8 library. See http://www.bsdua.org/libbsdua.html */ - +// UTF8 tests taken from Alexey Vatchenko's utf8 library. See http://www.bsdua.org/libbsdua.html. static void test_utf82wchar(const char *src, size_t slen, const wchar_t *dst, size_t dlen, - int flags, size_t res, const char *descr) -{ + int flags, size_t res, const char *descr) { size_t size; wchar_t *mem = NULL; - /* Hack: if wchar is only UCS-2, and the UTF-8 input string contains astral characters, then tweak the expected size to 0 */ - if (src != NULL && is_wchar_ucs2()) - { - /* A UTF-8 code unit may represent an astral code point if it has 4 or more leading 1s */ + // Hack: if wchar is only UCS-2, and the UTF-8 input string contains astral characters, then + // tweak the expected size to 0. + if (src != NULL && is_wchar_ucs2()) { + // A UTF-8 code unit may represent an astral code point if it has 4 or more leading 1s. const unsigned char astral_mask = 0xF0; - for (size_t i=0; i < slen; i++) - { - if ((src[i] & astral_mask) == astral_mask) - { - /* Astral char. We expect this conversion to just fail. */ + for (size_t i = 0; i < slen; i++) { + if ((src[i] & astral_mask) == astral_mask) { + // Astral char. We expect this conversion to just fail. res = 0; break; } } } - if (dst != NULL) - { + if (dst != NULL) { mem = (wchar_t *)malloc(dlen * sizeof(*mem)); - if (mem == NULL) - { + if (mem == NULL) { err(L"u2w: %s: MALLOC FAILED\n", descr); return; } } - do - { - if (mem == NULL) - { + do { + if (mem == NULL) { size = utf8_to_wchar(src, slen, NULL, flags); - } - else - { + } else { std::wstring buff; size = utf8_to_wchar(src, slen, &buff, flags); std::copy(buff.begin(), buff.begin() + std::min(dlen, buff.size()), mem); } - if (res != size) - { + if (res != size) { err(L"u2w: %s: FAILED (rv: %lu, must be %lu)", descr, size, res); break; } - if (mem == NULL) - break; /* OK */ + if (mem == NULL) break; /* OK */ - if (memcmp(mem, dst, size * sizeof(*mem)) != 0) - { + if (memcmp(mem, dst, size * sizeof(*mem)) != 0) { err(L"u2w: %s: BROKEN", descr); break; } - } - while (0); + } while (0); free(mem); } -// Annoying variant to handle uchar to avoid narrowing conversion warnings +// Annoying variant to handle uchar to avoid narrowing conversion warnings. static void test_utf82wchar(const unsigned char *usrc, size_t slen, const wchar_t *dst, size_t dlen, int flags, size_t res, const char *descr) { const char *src = reinterpret_cast(usrc); @@ -1090,19 +891,16 @@ static void test_utf82wchar(const unsigned char *usrc, size_t slen, const wchar_ } static void test_wchar2utf8(const wchar_t *src, size_t slen, const char *dst, size_t dlen, - int flags, size_t res, const char *descr) -{ + int flags, size_t res, const char *descr) { size_t size; char *mem = NULL; - /* Hack: if wchar is simulating UCS-2, and the wchar_t input string contains astral characters, then tweak the expected size to 0 */ - if (src != NULL && is_wchar_ucs2()) - { + // Hack: if wchar is simulating UCS-2, and the wchar_t input string contains astral characters, + // then tweak the expected size to 0. + if (src != NULL && is_wchar_ucs2()) { const uint32_t astral_mask = 0xFFFF0000U; - for (size_t i=0; i < slen; i++) - { - if ((src[i] & astral_mask) != 0) - { + for (size_t i = 0; i < slen; i++) { + if ((src[i] & astral_mask) != 0) { /* astral char */ res = 0; break; @@ -1110,46 +908,39 @@ static void test_wchar2utf8(const wchar_t *src, size_t slen, const char *dst, si } } - if (dst != NULL) - { + if (dst != NULL) { mem = (char *)malloc(dlen); - if (mem == NULL) - { + if (mem == NULL) { err(L"w2u: %s: MALLOC FAILED", descr); return; } } size = wchar_to_utf8(src, slen, mem, dlen, flags); - if (res != size) - { + if (res != size) { err(L"w2u: %s: FAILED (rv: %lu, must be %lu)", descr, size, res); goto finish; } - if (mem == NULL) - goto finish; /* OK */ + if (mem == NULL) goto finish; /* OK */ - if (memcmp(mem, dst, size) != 0) - { + if (memcmp(mem, dst, size) != 0) { err(L"w2u: %s: BROKEN", descr); goto finish; } - finish: +finish: free(mem); } -// Annoying variant to handle uchar to avoid narrowing conversion warnings +// Annoying variant to handle uchar to avoid narrowing conversion warnings. static void test_wchar2utf8(const wchar_t *src, size_t slen, const unsigned char *udst, size_t dlen, - int flags, size_t res, const char *descr) -{ + int flags, size_t res, const char *descr) { const char *dst = reinterpret_cast(udst); return test_wchar2utf8(src, slen, dst, dlen, flags, res, descr); } -static void test_utf8() -{ +static void test_utf8() { wchar_t w1[] = {0x54, 0x65, 0x73, 0x74}; wchar_t w2[] = {0x0422, 0x0435, 0x0441, 0x0442}; wchar_t w3[] = {0x800, 0x1e80, 0x98c4, 0x9910, 0xff00}; @@ -1165,226 +956,191 @@ static void test_utf8() wchar_t wbom22[] = {0xfeff, 0x41, 0xa}; unsigned char u1[] = {0x54, 0x65, 0x73, 0x74}; unsigned char u2[] = {0xd0, 0xa2, 0xd0, 0xb5, 0xd1, 0x81, 0xd1, 0x82}; - unsigned char u3[] = {0xe0, 0xa0, 0x80, 0xe1, 0xba, 0x80, 0xe9, 0xa3, 0x84, - 0xe9, 0xa4, 0x90, 0xef, 0xbc, 0x80 - }; + unsigned char u3[] = {0xe0, 0xa0, 0x80, 0xe1, 0xba, 0x80, 0xe9, 0xa3, + 0x84, 0xe9, 0xa4, 0x90, 0xef, 0xbc, 0x80}; unsigned char u4[] = {0xf0, 0x95, 0x95, 0x95, 0xf3, 0xb7, 0x9d, 0xb7, 0xa}; - unsigned char u5[] = {0xf8, 0x89, 0x95, 0x95, 0x95, 0xf9, 0xbe, 0xa0, 0x93, - 0xbf, 0xf8, 0xb7, 0x9f, 0xb4, 0x84, 0x0a - }; - unsigned char u6[] = {0xfc, 0x8f, 0x89, 0x95, 0x95, 0x95, 0xfc, 0x9d, 0xbe, - 0xa0, 0x93, 0xbf, 0xfd, 0xbf, 0xb7, 0x9f, 0xb4, 0x84, 0x0a - }; + unsigned char u5[] = {0xf8, 0x89, 0x95, 0x95, 0x95, 0xf9, 0xbe, 0xa0, + 0x93, 0xbf, 0xf8, 0xb7, 0x9f, 0xb4, 0x84, 0x0a}; + unsigned char u6[] = {0xfc, 0x8f, 0x89, 0x95, 0x95, 0x95, 0xfc, 0x9d, 0xbe, 0xa0, + 0x93, 0xbf, 0xfd, 0xbf, 0xb7, 0x9f, 0xb4, 0x84, 0x0a}; unsigned char ub[] = {0xa, 0xd1, 0x81}; unsigned char um[] = {0x41, 0xd1, 0x81, 0xe3, 0x81, 0x82, 0xef, 0xbd, 0xa7, - 0xe9, 0xac, 0x8d, 0xfc, 0xae, 0x81, 0x9d, 0xa9, 0xa7 - }; + 0xe9, 0xac, 0x8d, 0xfc, 0xae, 0x81, 0x9d, 0xa9, 0xa7}; unsigned char ub1[] = {0xa, 0xff, 0xd0, 0xa2, 0xfe, 0x8f, 0xe0, 0x80}; unsigned char uc080[] = {0xc0, 0x80}; unsigned char ub2[] = {0xed, 0xa1, 0x8c, 0xed, 0xbe, 0xb4, 0xa}; unsigned char ubom[] = {0x41, 0xa}; unsigned char ubom2[] = {0xef, 0xbb, 0xbf, 0x41, 0xa}; - /* - * UTF-8 -> UCS-4 string. - */ - test_utf82wchar(ubom2, sizeof(ubom2), wbom2, - sizeof(wbom2) / sizeof(*wbom2), UTF8_SKIP_BOM, + // UTF-8 -> UCS-4 string. + test_utf82wchar(ubom2, sizeof(ubom2), wbom2, sizeof(wbom2) / sizeof(*wbom2), UTF8_SKIP_BOM, sizeof(wbom2) / sizeof(*wbom2), "skip BOM"); - test_utf82wchar(ubom2, sizeof(ubom2), wbom22, - sizeof(wbom22) / sizeof(*wbom22), 0, + test_utf82wchar(ubom2, sizeof(ubom2), wbom22, sizeof(wbom22) / sizeof(*wbom22), 0, sizeof(wbom22) / sizeof(*wbom22), "BOM"); - test_utf82wchar(uc080, sizeof(uc080), NULL, 0, 0, 0, - "c0 80 - forbitten by rfc3629"); + test_utf82wchar(uc080, sizeof(uc080), NULL, 0, 0, 0, "c0 80 - forbitten by rfc3629"); test_utf82wchar(ub2, sizeof(ub2), NULL, 0, 0, is_wchar_ucs2() ? 0 : 3, "resulted in forbitten wchars (len)"); test_utf82wchar(ub2, sizeof(ub2), wb2, sizeof(wb2) / sizeof(*wb2), 0, 0, "resulted in forbitten wchars"); - test_utf82wchar(ub2, sizeof(ub2), L"\x0a", 1, UTF8_IGNORE_ERROR, - 1, "resulted in ignored forbitten wchars"); - test_utf82wchar(u1, sizeof(u1), w1, sizeof(w1) / sizeof(*w1), 0, - sizeof(w1) / sizeof(*w1), "1 octet chars"); - test_utf82wchar(u2, sizeof(u2), w2, sizeof(w2) / sizeof(*w2), 0, - sizeof(w2) / sizeof(*w2), "2 octets chars"); - test_utf82wchar(u3, sizeof(u3), w3, sizeof(w3) / sizeof(*w3), 0, - sizeof(w3) / sizeof(*w3), "3 octets chars"); - test_utf82wchar(u4, sizeof(u4), w4, sizeof(w4) / sizeof(*w4), 0, - sizeof(w4) / sizeof(*w4), "4 octets chars"); - test_utf82wchar(u5, sizeof(u5), w5, sizeof(w5) / sizeof(*w5), 0, - sizeof(w5) / sizeof(*w5), "5 octets chars"); - test_utf82wchar(u6, sizeof(u6), w6, sizeof(w6) / sizeof(*w6), 0, - sizeof(w6) / sizeof(*w6), "6 octets chars"); + test_utf82wchar(ub2, sizeof(ub2), L"\x0a", 1, UTF8_IGNORE_ERROR, 1, + "resulted in ignored forbitten wchars"); + test_utf82wchar(u1, sizeof(u1), w1, sizeof(w1) / sizeof(*w1), 0, sizeof(w1) / sizeof(*w1), + "1 octet chars"); + test_utf82wchar(u2, sizeof(u2), w2, sizeof(w2) / sizeof(*w2), 0, sizeof(w2) / sizeof(*w2), + "2 octets chars"); + test_utf82wchar(u3, sizeof(u3), w3, sizeof(w3) / sizeof(*w3), 0, sizeof(w3) / sizeof(*w3), + "3 octets chars"); + test_utf82wchar(u4, sizeof(u4), w4, sizeof(w4) / sizeof(*w4), 0, sizeof(w4) / sizeof(*w4), + "4 octets chars"); + test_utf82wchar(u5, sizeof(u5), w5, sizeof(w5) / sizeof(*w5), 0, sizeof(w5) / sizeof(*w5), + "5 octets chars"); + test_utf82wchar(u6, sizeof(u6), w6, sizeof(w6) / sizeof(*w6), 0, sizeof(w6) / sizeof(*w6), + "6 octets chars"); test_utf82wchar("\xff", 1, NULL, 0, 0, 0, "broken utf-8 0xff symbol"); test_utf82wchar("\xfe", 1, NULL, 0, 0, 0, "broken utf-8 0xfe symbol"); - test_utf82wchar("\x8f", 1, NULL, 0, 0, 0, - "broken utf-8, start from 10 higher bits"); - if (! is_wchar_ucs2()) test_utf82wchar(ub1, sizeof(ub1), wb1, sizeof(wb1) / sizeof(*wb1), - UTF8_IGNORE_ERROR, sizeof(wb1) / sizeof(*wb1), "ignore bad chars"); - test_utf82wchar(um, sizeof(um), wm, sizeof(wm) / sizeof(*wm), 0, - sizeof(wm) / sizeof(*wm), "mixed languages"); + test_utf82wchar("\x8f", 1, NULL, 0, 0, 0, "broken utf-8, start from 10 higher bits"); + if (!is_wchar_ucs2()) + test_utf82wchar(ub1, sizeof(ub1), wb1, sizeof(wb1) / sizeof(*wb1), UTF8_IGNORE_ERROR, + sizeof(wb1) / sizeof(*wb1), "ignore bad chars"); + test_utf82wchar(um, sizeof(um), wm, sizeof(wm) / sizeof(*wm), 0, sizeof(wm) / sizeof(*wm), + "mixed languages"); // PCA this test was to ensure that if the output buffer was too small, we'd get 0 // we no longer have statically sized result buffers, so this test is disabled // test_utf82wchar(um, sizeof(um), wm, sizeof(wm) / sizeof(*wm) - 1, 0, // 0, "boundaries -1"); - test_utf82wchar(um, sizeof(um), wm, sizeof(wm) / sizeof(*wm) + 1, 0, - sizeof(wm) / sizeof(*wm), "boundaries +1"); - test_utf82wchar(um, sizeof(um), NULL, 0, 0, - sizeof(wm) / sizeof(*wm), "calculate length"); - test_utf82wchar(ub1, sizeof(ub1), NULL, 0, 0, - 0, "calculate length of bad chars"); - test_utf82wchar(ub1, sizeof(ub1), NULL, 0, - UTF8_IGNORE_ERROR, sizeof(wb1) / sizeof(*wb1), + test_utf82wchar(um, sizeof(um), wm, sizeof(wm) / sizeof(*wm) + 1, 0, sizeof(wm) / sizeof(*wm), + "boundaries +1"); + test_utf82wchar(um, sizeof(um), NULL, 0, 0, sizeof(wm) / sizeof(*wm), "calculate length"); + test_utf82wchar(ub1, sizeof(ub1), NULL, 0, 0, 0, "calculate length of bad chars"); + test_utf82wchar(ub1, sizeof(ub1), NULL, 0, UTF8_IGNORE_ERROR, sizeof(wb1) / sizeof(*wb1), "calculate length, ignore bad chars"); test_utf82wchar((const char *)NULL, 0, NULL, 0, 0, 0, "invalid params, all 0"); - test_utf82wchar(u1, 0, NULL, 0, 0, 0, - "invalid params, src buf not NULL"); - test_utf82wchar((const char *)NULL, 10, NULL, 0, 0, 0, - "invalid params, src length is not 0"); - + test_utf82wchar(u1, 0, NULL, 0, 0, 0, "invalid params, src buf not NULL"); + test_utf82wchar((const char *)NULL, 10, NULL, 0, 0, 0, "invalid params, src length is not 0"); + // PCA this test was to ensure that converting into a zero length output buffer would return 0 // we no longer statically size output buffers, so the test is disabled // test_utf82wchar(u1, sizeof(u1), w1, 0, 0, 0, // "invalid params, dst is not NULL"); - /* - * UCS-4 -> UTF-8 string. - */ - const char * const nullc = NULL; - test_wchar2utf8(wbom, sizeof(wbom) / sizeof(*wbom), ubom, sizeof(ubom), - UTF8_SKIP_BOM, sizeof(ubom), "BOM"); - test_wchar2utf8(wb2, sizeof(wb2) / sizeof(*wb2), nullc, 0, 0, - 0, "prohibited wchars"); - test_wchar2utf8(wb2, sizeof(wb2) / sizeof(*wb2), nullc, 0, - UTF8_IGNORE_ERROR, 2, "ignore prohibited wchars"); - test_wchar2utf8(w1, sizeof(w1) / sizeof(*w1), u1, sizeof(u1), 0, - sizeof(u1), "1 octet chars"); - test_wchar2utf8(w2, sizeof(w2) / sizeof(*w2), u2, sizeof(u2), 0, - sizeof(u2), "2 octets chars"); - test_wchar2utf8(w3, sizeof(w3) / sizeof(*w3), u3, sizeof(u3), 0, - sizeof(u3), "3 octets chars"); - test_wchar2utf8(w4, sizeof(w4) / sizeof(*w4), u4, sizeof(u4), 0, - sizeof(u4), "4 octets chars"); - test_wchar2utf8(w5, sizeof(w5) / sizeof(*w5), u5, sizeof(u5), 0, - sizeof(u5), "5 octets chars"); - test_wchar2utf8(w6, sizeof(w6) / sizeof(*w6), u6, sizeof(u6), 0, - sizeof(u6), "6 octets chars"); - test_wchar2utf8(wb, sizeof(wb) / sizeof(*wb), ub, sizeof(ub), 0, - 0, "bad chars"); - test_wchar2utf8(wb, sizeof(wb) / sizeof(*wb), ub, sizeof(ub), - UTF8_IGNORE_ERROR, sizeof(ub), "ignore bad chars"); - test_wchar2utf8(wm, sizeof(wm) / sizeof(*wm), um, sizeof(um), 0, - sizeof(um), "mixed languages"); - test_wchar2utf8(wm, sizeof(wm) / sizeof(*wm), um, sizeof(um) - 1, 0, - 0, "boundaries -1"); - test_wchar2utf8(wm, sizeof(wm) / sizeof(*wm), um, sizeof(um) + 1, 0, - sizeof(um), "boundaries +1"); - test_wchar2utf8(wm, sizeof(wm) / sizeof(*wm), nullc, 0, 0, - sizeof(um), "calculate length"); - test_wchar2utf8(wb, sizeof(wb) / sizeof(*wb), nullc, 0, 0, - 0, "calculate length of bad chars"); - test_wchar2utf8(wb, sizeof(wb) / sizeof(*wb), nullc, 0, - UTF8_IGNORE_ERROR, sizeof(ub), + // UCS-4 -> UTF-8 string. + const char *const nullc = NULL; + test_wchar2utf8(wbom, sizeof(wbom) / sizeof(*wbom), ubom, sizeof(ubom), UTF8_SKIP_BOM, + sizeof(ubom), "BOM"); + test_wchar2utf8(wb2, sizeof(wb2) / sizeof(*wb2), nullc, 0, 0, 0, "prohibited wchars"); + test_wchar2utf8(wb2, sizeof(wb2) / sizeof(*wb2), nullc, 0, UTF8_IGNORE_ERROR, 2, + "ignore prohibited wchars"); + test_wchar2utf8(w1, sizeof(w1) / sizeof(*w1), u1, sizeof(u1), 0, sizeof(u1), "1 octet chars"); + test_wchar2utf8(w2, sizeof(w2) / sizeof(*w2), u2, sizeof(u2), 0, sizeof(u2), "2 octets chars"); + test_wchar2utf8(w3, sizeof(w3) / sizeof(*w3), u3, sizeof(u3), 0, sizeof(u3), "3 octets chars"); + test_wchar2utf8(w4, sizeof(w4) / sizeof(*w4), u4, sizeof(u4), 0, sizeof(u4), "4 octets chars"); + test_wchar2utf8(w5, sizeof(w5) / sizeof(*w5), u5, sizeof(u5), 0, sizeof(u5), "5 octets chars"); + test_wchar2utf8(w6, sizeof(w6) / sizeof(*w6), u6, sizeof(u6), 0, sizeof(u6), "6 octets chars"); + test_wchar2utf8(wb, sizeof(wb) / sizeof(*wb), ub, sizeof(ub), 0, 0, "bad chars"); + test_wchar2utf8(wb, sizeof(wb) / sizeof(*wb), ub, sizeof(ub), UTF8_IGNORE_ERROR, sizeof(ub), + "ignore bad chars"); + test_wchar2utf8(wm, sizeof(wm) / sizeof(*wm), um, sizeof(um), 0, sizeof(um), "mixed languages"); + test_wchar2utf8(wm, sizeof(wm) / sizeof(*wm), um, sizeof(um) - 1, 0, 0, "boundaries -1"); + test_wchar2utf8(wm, sizeof(wm) / sizeof(*wm), um, sizeof(um) + 1, 0, sizeof(um), + "boundaries +1"); + test_wchar2utf8(wm, sizeof(wm) / sizeof(*wm), nullc, 0, 0, sizeof(um), "calculate length"); + test_wchar2utf8(wb, sizeof(wb) / sizeof(*wb), nullc, 0, 0, 0, "calculate length of bad chars"); + test_wchar2utf8(wb, sizeof(wb) / sizeof(*wb), nullc, 0, UTF8_IGNORE_ERROR, sizeof(ub), "calculate length, ignore bad chars"); test_wchar2utf8(NULL, 0, nullc, 0, 0, 0, "invalid params, all 0"); - test_wchar2utf8(w1, 0, nullc, 0, 0, 0, - "invalid params, src buf not NULL"); - test_wchar2utf8(NULL, 10, nullc, 0, 0, 0, - "invalid params, src length is not 0"); - test_wchar2utf8(w1, sizeof(w1) / sizeof(*w1), u1, 0, 0, 0, - "invalid params, dst is not NULL"); + test_wchar2utf8(w1, 0, nullc, 0, 0, 0, "invalid params, src buf not NULL"); + test_wchar2utf8(NULL, 10, nullc, 0, 0, 0, "invalid params, src length is not 0"); + test_wchar2utf8(w1, sizeof(w1) / sizeof(*w1), u1, 0, 0, 0, "invalid params, dst is not NULL"); } -static void test_escape_sequences(void) -{ +static void test_escape_sequences(void) { say(L"Testing escape codes"); if (escape_code_length(L"") != 0) err(L"test_escape_sequences failed on line %d\n", __LINE__); - if (escape_code_length(L"abcd") != 0) err(L"test_escape_sequences failed on line %d\n", __LINE__); - if (escape_code_length(L"\x1b[2J") != 4) err(L"test_escape_sequences failed on line %d\n", __LINE__); - if (escape_code_length(L"\x1b[38;5;123mABC") != strlen("\x1b[38;5;123m")) err(L"test_escape_sequences failed on line %d\n", __LINE__); - if (escape_code_length(L"\x1b@") != 2) err(L"test_escape_sequences failed on line %d\n", __LINE__); + if (escape_code_length(L"abcd") != 0) + err(L"test_escape_sequences failed on line %d\n", __LINE__); + if (escape_code_length(L"\x1b[2J") != 4) + err(L"test_escape_sequences failed on line %d\n", __LINE__); + if (escape_code_length(L"\x1b[38;5;123mABC") != strlen("\x1b[38;5;123m")) + err(L"test_escape_sequences failed on line %d\n", __LINE__); + if (escape_code_length(L"\x1b@") != 2) + err(L"test_escape_sequences failed on line %d\n", __LINE__); - // iTerm2 escape sequences - if (escape_code_length(L"\x1b]50;CurrentDir=/tmp/foo\x07NOT_PART_OF_SEQUENCE") != 25) err(L"test_escape_sequences failed on line %d\n", __LINE__); - if (escape_code_length(L"\x1b]50;SetMark\x07NOT_PART_OF_SEQUENCE") != 13) err(L"test_escape_sequences failed on line %d\n", __LINE__); - if (escape_code_length(L"\x1b" L"]6;1;bg;red;brightness;255\x07NOT_PART_OF_SEQUENCE") != 28) err(L"test_escape_sequences failed on line %d\n", __LINE__); - if (escape_code_length(L"\x1b]Pg4040ff\x1b\\NOT_PART_OF_SEQUENCE") != 12) err(L"test_escape_sequences failed on line %d\n", __LINE__); - if (escape_code_length(L"\x1b]blahblahblah\x1b\\") != 16) err(L"test_escape_sequences failed on line %d\n", __LINE__); - if (escape_code_length(L"\x1b]blahblahblah\x07") != 15) err(L"test_escape_sequences failed on line %d\n", __LINE__); + // iTerm2 escape sequences. + if (escape_code_length(L"\x1b]50;CurrentDir=/tmp/foo\x07NOT_PART_OF_SEQUENCE") != 25) + err(L"test_escape_sequences failed on line %d\n", __LINE__); + if (escape_code_length(L"\x1b]50;SetMark\x07NOT_PART_OF_SEQUENCE") != 13) + err(L"test_escape_sequences failed on line %d\n", __LINE__); + if (escape_code_length(L"\x1b" + L"]6;1;bg;red;brightness;255\x07NOT_PART_OF_SEQUENCE") != 28) + err(L"test_escape_sequences failed on line %d\n", __LINE__); + if (escape_code_length(L"\x1b]Pg4040ff\x1b\\NOT_PART_OF_SEQUENCE") != 12) + err(L"test_escape_sequences failed on line %d\n", __LINE__); + if (escape_code_length(L"\x1b]blahblahblah\x1b\\") != 16) + err(L"test_escape_sequences failed on line %d\n", __LINE__); + if (escape_code_length(L"\x1b]blahblahblah\x07") != 15) + err(L"test_escape_sequences failed on line %d\n", __LINE__); } -class lru_node_test_t : public lru_node_t -{ -public: - explicit lru_node_test_t(const wcstring &tmp) : lru_node_t(tmp) { } +class lru_node_test_t : public lru_node_t { + public: + explicit lru_node_test_t(const wcstring &tmp) : lru_node_t(tmp) {} }; -class test_lru_t : public lru_cache_t -{ -public: - test_lru_t() : lru_cache_t(16) { } +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) { do_test(find(evicted_nodes.begin(), evicted_nodes.end(), node) == evicted_nodes.end()); evicted_nodes.push_back(node); } }; -static void test_lru(void) -{ +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++) { do_test(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); - // Adding the node the first time should work, and subsequent times should fail + // Adding the node the first time should work, and subsequent times should fail. do_test(cache.add_node(node)); - do_test(! cache.add_node(node)); + do_test(!cache.add_node(node)); } do_test(cache.evicted_nodes == expected_evicted); cache.evict_all_nodes(); do_test(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; } } -/** - Perform parameter expansion and test if the output equals the zero-terminated parameter list supplied. - - \param in the string to expand - \param flags the flags to send to expand_string - \param ... A zero-terminated parameter list of values to test. - After the zero terminator comes one more arg, a string, which is the error - message to print if the test fails. -*/ - -static bool expand_test(const wchar_t *in, expand_flags_t flags, ...) -{ +/// Perform parameter expansion and test if the output equals the zero-terminated parameter list +/// supplied. +/// +/// \param in the string to expand +/// \param flags the flags to send to expand_string +/// \param ... A zero-terminated parameter list of values to test. +/// After the zero terminator comes one more arg, a string, which is the error +/// message to print if the test fails. +static bool expand_test(const wchar_t *in, expand_flags_t flags, ...) { std::vector output; va_list va; - bool res=true; + bool res = true; wchar_t *arg; parse_error_list_t errors; - if (expand_string(in, &output, flags, &errors) == EXPAND_ERROR) - { - if (errors.empty()) - { + if (expand_string(in, &output, flags, &errors) == EXPAND_ERROR) { + if (errors.empty()) { err(L"Bug: Parse error reported but no error text found."); - } - else - { + } else { err(L"%ls", errors.at(0).describe(wcstring(in)).c_str()); } return false; @@ -1393,35 +1149,29 @@ static bool expand_test(const wchar_t *in, expand_flags_t flags, ...) wcstring_list_t expected; va_start(va, flags); - while ((arg=va_arg(va, wchar_t *))!= 0) - { + while ((arg = va_arg(va, wchar_t *)) != 0) { expected.push_back(wcstring(arg)); } va_end(va); std::set remaining(expected.begin(), expected.end()); std::vector::const_iterator out_it = output.begin(), out_end = output.end(); - for (; out_it != out_end; ++out_it) - { - if (! remaining.erase(out_it->completion)) - { + for (; out_it != out_end; ++out_it) { + if (!remaining.erase(out_it->completion)) { res = false; break; } } - if (! remaining.empty()) - { + if (!remaining.empty()) { res = false; } - if (!res) - { - if ((arg = va_arg(va, wchar_t *)) != 0) - { + if (!res) { + if ((arg = va_arg(va, wchar_t *)) != 0) { wcstring msg = L"Expected ["; bool first = true; - for (wcstring_list_t::const_iterator it = expected.begin(), end = expected.end(); it != end; ++it) - { + for (wcstring_list_t::const_iterator it = expected.begin(), end = expected.end(); + it != end; ++it) { if (!first) msg += L", "; first = false; msg += '"'; @@ -1430,8 +1180,8 @@ static bool expand_test(const wchar_t *in, expand_flags_t flags, ...) } msg += L"], found ["; first = true; - for (std::vector::const_iterator it = output.begin(), end = output.end(); it != end; ++it) - { + for (std::vector::const_iterator it = output.begin(), end = output.end(); + it != end; ++it) { if (!first) msg += L", "; first = false; msg += '"'; @@ -1446,24 +1196,17 @@ static bool expand_test(const wchar_t *in, expand_flags_t flags, ...) va_end(va); return res; - } -/** - Test globbing and other parameter expansion -*/ -static void test_expand() -{ +/// Test globbing and other parameter expansion. +static void test_expand() { say(L"Testing parameter expansion"); - expand_test(L"foo", 0, L"foo", 0, - L"Strings do not expand to themselves"); + expand_test(L"foo", 0, L"foo", 0, L"Strings do not expand to themselves"); - expand_test(L"a{b,c,d}e", 0, L"abe", L"ace", L"ade", 0, - L"Bracket expansion is broken"); + expand_test(L"a{b,c,d}e", 0, L"abe", L"ace", L"ade", 0, L"Bracket expansion is broken"); - expand_test(L"a*", EXPAND_SKIP_WILDCARDS, L"a*", 0, - L"Cannot skip wildcard expansion"); + expand_test(L"a*", EXPAND_SKIP_WILDCARDS, L"a*", 0, L"Cannot skip wildcard expansion"); expand_test(L"/bin/l\\0", EXPAND_FOR_COMPLETIONS, 0, L"Failed to handle null escape in expansion"); @@ -1471,22 +1214,18 @@ static void test_expand() expand_test(L"foo\\$bar", EXPAND_SKIP_VARIABLES, L"foo$bar", 0, L"Failed to handle dollar sign in variable-skipping expansion"); - - /* - b - x - bar - baz - xxx - yyy - bax - xxx - lol - nub - q - .foo - */ - + // b + // x + // bar + // baz + // xxx + // yyy + // bax + // xxx + // lol + // nub + // q + // .foo if (system("mkdir -p /tmp/fish_expand_test/")) err(L"mkdir failed"); if (system("mkdir -p /tmp/fish_expand_test/b/")) err(L"mkdir failed"); if (system("mkdir -p /tmp/fish_expand_test/baz/")) err(L"mkdir failed"); @@ -1500,122 +1239,119 @@ static void test_expand() if (system("touch /tmp/fish_expand_test/baz/yyy")) err(L"touch failed"); if (system("touch /tmp/fish_expand_test/lol/nub/q")) 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 "*" - const wchar_t * const wnull = NULL; - expand_test(L"/tmp/fish_expand_test/.*", 0, - L"/tmp/fish_expand_test/.foo", wnull, + // 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 "*". + const wchar_t *const wnull = NULL; + expand_test(L"/tmp/fish_expand_test/.*", 0, L"/tmp/fish_expand_test/.foo", wnull, L"Expansion not correctly handling dotfiles"); - expand_test(L"/tmp/fish_expand_test/./.*", 0, - L"/tmp/fish_expand_test/./.foo", wnull, + expand_test(L"/tmp/fish_expand_test/./.*", 0, L"/tmp/fish_expand_test/./.foo", wnull, L"Expansion not correctly handling literal path components in dotfiles"); - expand_test(L"/tmp/fish_expand_test/*/xxx", 0, - L"/tmp/fish_expand_test/bax/xxx", L"/tmp/fish_expand_test/baz/xxx", wnull, - L"Glob did the wrong thing 1"); + expand_test(L"/tmp/fish_expand_test/*/xxx", 0, L"/tmp/fish_expand_test/bax/xxx", + L"/tmp/fish_expand_test/baz/xxx", wnull, L"Glob did the wrong thing 1"); - expand_test(L"/tmp/fish_expand_test/*z/xxx", 0, - L"/tmp/fish_expand_test/baz/xxx", wnull, + expand_test(L"/tmp/fish_expand_test/*z/xxx", 0, L"/tmp/fish_expand_test/baz/xxx", wnull, L"Glob did the wrong thing 2"); - expand_test(L"/tmp/fish_expand_test/**z/xxx", 0, - L"/tmp/fish_expand_test/baz/xxx", wnull, + expand_test(L"/tmp/fish_expand_test/**z/xxx", 0, L"/tmp/fish_expand_test/baz/xxx", wnull, L"Glob did the wrong thing 3"); - expand_test(L"/tmp/fish_expand_test/b**", 0, - L"/tmp/fish_expand_test/b", L"/tmp/fish_expand_test/b/x", L"/tmp/fish_expand_test/bar", L"/tmp/fish_expand_test/bax", L"/tmp/fish_expand_test/bax/xxx", L"/tmp/fish_expand_test/baz", L"/tmp/fish_expand_test/baz/xxx", L"/tmp/fish_expand_test/baz/yyy", wnull, - L"Glob did the wrong thing 4"); - - // a trailing slash should only produce directories - expand_test(L"/tmp/fish_expand_test/b*/", 0, - L"/tmp/fish_expand_test/b/", L"/tmp/fish_expand_test/baz/", L"/tmp/fish_expand_test/bax/", wnull, + expand_test(L"/tmp/fish_expand_test/b**", 0, L"/tmp/fish_expand_test/b", + L"/tmp/fish_expand_test/b/x", L"/tmp/fish_expand_test/bar", + L"/tmp/fish_expand_test/bax", L"/tmp/fish_expand_test/bax/xxx", + L"/tmp/fish_expand_test/baz", L"/tmp/fish_expand_test/baz/xxx", + L"/tmp/fish_expand_test/baz/yyy", wnull, L"Glob did the wrong thing 4"); + + // A trailing slash should only produce directories. + expand_test(L"/tmp/fish_expand_test/b*/", 0, L"/tmp/fish_expand_test/b/", + L"/tmp/fish_expand_test/baz/", L"/tmp/fish_expand_test/bax/", wnull, L"Glob did the wrong thing 5"); - expand_test(L"/tmp/fish_expand_test/b**/", 0, - L"/tmp/fish_expand_test/b/", L"/tmp/fish_expand_test/baz/", L"/tmp/fish_expand_test/bax/", wnull, + expand_test(L"/tmp/fish_expand_test/b**/", 0, L"/tmp/fish_expand_test/b/", + L"/tmp/fish_expand_test/baz/", L"/tmp/fish_expand_test/bax/", wnull, L"Glob did the wrong thing 6"); - - expand_test(L"/tmp/fish_expand_test/**/q", 0, - L"/tmp/fish_expand_test/lol/nub/q", wnull, + + expand_test(L"/tmp/fish_expand_test/**/q", 0, L"/tmp/fish_expand_test/lol/nub/q", wnull, L"Glob did the wrong thing 7"); - - expand_test(L"/tmp/fish_expand_test/BA", EXPAND_FOR_COMPLETIONS, - L"/tmp/fish_expand_test/bar", L"/tmp/fish_expand_test/bax/", L"/tmp/fish_expand_test/baz/", wnull, + + expand_test(L"/tmp/fish_expand_test/BA", EXPAND_FOR_COMPLETIONS, L"/tmp/fish_expand_test/bar", + L"/tmp/fish_expand_test/bax/", L"/tmp/fish_expand_test/baz/", wnull, L"Case insensitive test did the wrong thing"); - expand_test(L"/tmp/fish_expand_test/BA", EXPAND_FOR_COMPLETIONS, - L"/tmp/fish_expand_test/bar", L"/tmp/fish_expand_test/bax/", L"/tmp/fish_expand_test/baz/", wnull, + expand_test(L"/tmp/fish_expand_test/BA", EXPAND_FOR_COMPLETIONS, L"/tmp/fish_expand_test/bar", + L"/tmp/fish_expand_test/bax/", L"/tmp/fish_expand_test/baz/", wnull, L"Case insensitive test did the wrong thing"); expand_test(L"/tmp/fish_expand_test/b/yyy", EXPAND_FOR_COMPLETIONS, - /* nothing! */ wnull, - L"Wrong fuzzy matching 1"); + /* nothing! */ wnull, L"Wrong fuzzy matching 1"); - expand_test(L"/tmp/fish_expand_test/b/x", EXPAND_FOR_COMPLETIONS | EXPAND_FUZZY_MATCH, - L"", wnull, // We just expect the empty string since this is an exact match + expand_test(L"/tmp/fish_expand_test/b/x", EXPAND_FOR_COMPLETIONS | EXPAND_FUZZY_MATCH, L"", + wnull, // we just expect the empty string since this is an exact match L"Wrong fuzzy matching 2"); - // some vswprintfs refuse to append ANY_STRING in a format specifiers, so don't use format_string here + // Some vswprintfs refuse to append ANY_STRING in a format specifiers, so don't use + // format_string here. const wcstring any_str_str(1, ANY_STRING); expand_test(L"/tmp/fish_expand_test/b/xx*", EXPAND_FOR_COMPLETIONS | EXPAND_FUZZY_MATCH, - (L"/tmp/fish_expand_test/bax/xx" + any_str_str).c_str(), (L"/tmp/fish_expand_test/baz/xx" + any_str_str).c_str(), wnull, + (L"/tmp/fish_expand_test/bax/xx" + any_str_str).c_str(), + (L"/tmp/fish_expand_test/baz/xx" + any_str_str).c_str(), wnull, L"Wrong fuzzy matching 3"); expand_test(L"/tmp/fish_expand_test/b/yyy", EXPAND_FOR_COMPLETIONS | EXPAND_FUZZY_MATCH, - L"/tmp/fish_expand_test/baz/yyy", wnull, - L"Wrong fuzzy matching 4"); + L"/tmp/fish_expand_test/baz/yyy", wnull, L"Wrong fuzzy matching 4"); - if (! expand_test(L"/tmp/fish_expand_test/.*", 0, L"/tmp/fish_expand_test/.foo", 0)) - { + 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)) - { + 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"); } char saved_wd[PATH_MAX] = {}; - if (NULL == getcwd(saved_wd, sizeof saved_wd)) - { + if (NULL == getcwd(saved_wd, sizeof saved_wd)) { err(L"getcwd failed"); return; } - if (chdir_set_pwd("/tmp/fish_expand_test")) - { + if (chdir_set_pwd("/tmp/fish_expand_test")) { err(L"chdir failed"); return; } - expand_test(L"b/xx", EXPAND_FOR_COMPLETIONS | EXPAND_FUZZY_MATCH, - L"bax/xxx", L"baz/xxx", wnull, + expand_test(L"b/xx", EXPAND_FOR_COMPLETIONS | EXPAND_FUZZY_MATCH, L"bax/xxx", L"baz/xxx", wnull, L"Wrong fuzzy matching 5"); - if (chdir_set_pwd(saved_wd)) - { + if (chdir_set_pwd(saved_wd)) { err(L"chdir failed"); } - if (system("rm -Rf /tmp/fish_expand_test")) err(L"rm failed"); } -static void test_fuzzy_match(void) -{ +static void test_fuzzy_match(void) { say(L"Testing fuzzy string matching"); - if (string_fuzzy_match_string(L"", L"").type != fuzzy_match_exact) err(L"test_fuzzy_match failed on line %ld", __LINE__); - if (string_fuzzy_match_string(L"alpha", L"alpha").type != fuzzy_match_exact) err(L"test_fuzzy_match failed on line %ld", __LINE__); - if (string_fuzzy_match_string(L"alp", L"alpha").type != fuzzy_match_prefix) err(L"test_fuzzy_match failed on line %ld", __LINE__); - if (string_fuzzy_match_string(L"ALPHA!", L"alPhA!").type != fuzzy_match_case_insensitive) err(L"test_fuzzy_match failed on line %ld", __LINE__); - if (string_fuzzy_match_string(L"alPh", L"ALPHA!").type != fuzzy_match_prefix_case_insensitive) err(L"test_fuzzy_match failed on line %ld", __LINE__); - if (string_fuzzy_match_string(L"LPH", L"ALPHA!").type != fuzzy_match_substring) err(L"test_fuzzy_match failed on line %ld", __LINE__); - if (string_fuzzy_match_string(L"AA", L"ALPHA!").type != fuzzy_match_subsequence_insertions_only) err(L"test_fuzzy_match failed on line %ld", __LINE__); - if (string_fuzzy_match_string(L"BB", L"ALPHA!").type != fuzzy_match_none) err(L"test_fuzzy_match failed on line %ld", __LINE__); + if (string_fuzzy_match_string(L"", L"").type != fuzzy_match_exact) + err(L"test_fuzzy_match failed on line %ld", __LINE__); + if (string_fuzzy_match_string(L"alpha", L"alpha").type != fuzzy_match_exact) + err(L"test_fuzzy_match failed on line %ld", __LINE__); + if (string_fuzzy_match_string(L"alp", L"alpha").type != fuzzy_match_prefix) + err(L"test_fuzzy_match failed on line %ld", __LINE__); + if (string_fuzzy_match_string(L"ALPHA!", L"alPhA!").type != fuzzy_match_case_insensitive) + err(L"test_fuzzy_match failed on line %ld", __LINE__); + if (string_fuzzy_match_string(L"alPh", L"ALPHA!").type != fuzzy_match_prefix_case_insensitive) + err(L"test_fuzzy_match failed on line %ld", __LINE__); + if (string_fuzzy_match_string(L"LPH", L"ALPHA!").type != fuzzy_match_substring) + err(L"test_fuzzy_match failed on line %ld", __LINE__); + if (string_fuzzy_match_string(L"AA", L"ALPHA!").type != fuzzy_match_subsequence_insertions_only) + err(L"test_fuzzy_match failed on line %ld", __LINE__); + if (string_fuzzy_match_string(L"BB", L"ALPHA!").type != fuzzy_match_none) + err(L"test_fuzzy_match failed on line %ld", __LINE__); } -static void test_abbreviations(void) -{ +static void test_abbreviations(void) { say(L"Testing abbreviations"); const wchar_t *abbreviations = @@ -1635,91 +1371,98 @@ static void test_abbreviations(void) wcstring result; if (expand_abbreviation(L"", &result)) err(L"Unexpected success with empty abbreviation"); - if (expand_abbreviation(L"nothing", &result)) err(L"Unexpected success with missing abbreviation"); + if (expand_abbreviation(L"nothing", &result)) + err(L"Unexpected success with missing abbreviation"); - if (! expand_abbreviation(L"gc", &result)) err(L"Unexpected failure with gc abbreviation"); + if (!expand_abbreviation(L"gc", &result)) err(L"Unexpected failure with gc abbreviation"); if (result != L"git checkout") err(L"Wrong abbreviation result for gc"); result.clear(); - if (! expand_abbreviation(L"foo", &result)) err(L"Unexpected failure with foo abbreviation"); + if (!expand_abbreviation(L"foo", &result)) err(L"Unexpected failure with foo abbreviation"); if (result != L"bar") err(L"Wrong abbreviation result for foo"); bool expanded; expanded = reader_expand_abbreviation_in_command(L"just a command", 3, &result); if (expanded) err(L"Command wrongly expanded on line %ld", (long)__LINE__); expanded = reader_expand_abbreviation_in_command(L"gc somebranch", 0, &result); - if (! expanded) err(L"Command not expanded on line %ld", (long)__LINE__); + if (!expanded) err(L"Command not expanded on line %ld", (long)__LINE__); expanded = reader_expand_abbreviation_in_command(L"gc somebranch", wcslen(L"gc"), &result); - if (! expanded) err(L"gc not expanded"); - if (result != L"git checkout somebranch") err(L"gc incorrectly expanded on line %ld to '%ls'", (long)__LINE__, result.c_str()); + if (!expanded) err(L"gc not expanded"); + if (result != L"git checkout somebranch") + err(L"gc incorrectly expanded on line %ld to '%ls'", (long)__LINE__, result.c_str()); - /* space separation */ + // Space separation. expanded = reader_expand_abbreviation_in_command(L"gx somebranch", wcslen(L"gc"), &result); - if (! expanded) err(L"gx not expanded"); - if (result != L"git checkout somebranch") err(L"gc incorrectly expanded on line %ld to '%ls'", (long)__LINE__, result.c_str()); + if (!expanded) err(L"gx not expanded"); + if (result != L"git checkout somebranch") + err(L"gc incorrectly expanded on line %ld to '%ls'", (long)__LINE__, result.c_str()); - expanded = reader_expand_abbreviation_in_command(L"echo hi ; gc somebranch", wcslen(L"echo hi ; g"), &result); - if (! expanded) err(L"gc not expanded on line %ld", (long)__LINE__); - if (result != L"echo hi ; git checkout somebranch") err(L"gc incorrectly expanded on line %ld", (long)__LINE__); + expanded = reader_expand_abbreviation_in_command(L"echo hi ; gc somebranch", + wcslen(L"echo hi ; g"), &result); + if (!expanded) err(L"gc not expanded on line %ld", (long)__LINE__); + if (result != L"echo hi ; git checkout somebranch") + err(L"gc incorrectly expanded on line %ld", (long)__LINE__); - expanded = reader_expand_abbreviation_in_command(L"echo (echo (echo (echo (gc ", wcslen(L"echo (echo (echo (echo (gc"), &result); - if (! expanded) err(L"gc not expanded on line %ld", (long)__LINE__); - if (result != L"echo (echo (echo (echo (git checkout ") err(L"gc incorrectly expanded on line %ld to '%ls'", (long)__LINE__, result.c_str()); + expanded = reader_expand_abbreviation_in_command( + L"echo (echo (echo (echo (gc ", wcslen(L"echo (echo (echo (echo (gc"), &result); + if (!expanded) err(L"gc not expanded on line %ld", (long)__LINE__); + if (result != L"echo (echo (echo (echo (git checkout ") + err(L"gc incorrectly expanded on line %ld to '%ls'", (long)__LINE__, result.c_str()); - /* if commands should be expanded */ + // If commands should be expanded. expanded = reader_expand_abbreviation_in_command(L"if gc", wcslen(L"if gc"), &result); - if (! expanded) err(L"gc not expanded on line %ld", (long)__LINE__); - if (result != L"if git checkout") err(L"gc incorrectly expanded on line %ld to '%ls'", (long)__LINE__, result.c_str()); + if (!expanded) err(L"gc not expanded on line %ld", (long)__LINE__); + if (result != L"if git checkout") + err(L"gc incorrectly expanded on line %ld to '%ls'", (long)__LINE__, result.c_str()); - /* others should not be */ + // Others should not be. expanded = reader_expand_abbreviation_in_command(L"of gc", wcslen(L"of gc"), &result); if (expanded) err(L"gc incorrectly expanded on line %ld", (long)__LINE__); - /* others should not be */ + // Others should not be. expanded = reader_expand_abbreviation_in_command(L"command gc", wcslen(L"command gc"), &result); if (expanded) err(L"gc incorrectly expanded on line %ld", (long)__LINE__); - env_pop(); } -/** Test path functions */ -static void test_path() -{ +/// Test path functions. +static void test_path() { say(L"Testing path functions"); wcstring path = L"//foo//////bar/"; path_make_canonical(path); - if (path != L"/foo/bar") - { + if (path != L"/foo/bar") { err(L"Bug in canonical PATH code"); } path = L"/"; path_make_canonical(path); - if (path != L"/") - { + if (path != L"/") { err(L"Bug in canonical PATH code"); } - if (paths_are_equivalent(L"/foo/bar/baz", L"foo/bar/baz")) err(L"Bug in canonical PATH code on line %ld", (long)__LINE__); - if (! paths_are_equivalent(L"///foo///bar/baz", L"/foo/bar////baz//")) err(L"Bug in canonical PATH code on line %ld", (long)__LINE__); - if (! paths_are_equivalent(L"/foo/bar/baz", L"/foo/bar/baz")) err(L"Bug in canonical PATH code on line %ld", (long)__LINE__); - if (! paths_are_equivalent(L"/", L"/")) err(L"Bug in canonical PATH code on line %ld", (long)__LINE__); + if (paths_are_equivalent(L"/foo/bar/baz", L"foo/bar/baz")) + err(L"Bug in canonical PATH code on line %ld", (long)__LINE__); + if (!paths_are_equivalent(L"///foo///bar/baz", L"/foo/bar////baz//")) + err(L"Bug in canonical PATH code on line %ld", (long)__LINE__); + if (!paths_are_equivalent(L"/foo/bar/baz", L"/foo/bar/baz")) + err(L"Bug in canonical PATH code on line %ld", (long)__LINE__); + if (!paths_are_equivalent(L"/", L"/")) + err(L"Bug in canonical PATH code on line %ld", (long)__LINE__); } -static void test_pager_navigation() -{ +static void test_pager_navigation() { say(L"Testing pager navigation"); - /* Generate 19 strings of width 10. There's 2 spaces between completions, and our term size is 80; these can therefore fit into 6 columns (6 * 12 - 2 = 70) or 5 columns (58) but not 7 columns (7 * 12 - 2 = 82). - - You can simulate this test by creating 19 files named "file00.txt" through "file_18.txt". - */ + // Generate 19 strings of width 10. There's 2 spaces between completions, and our term size is + // 80; these can therefore fit into 6 columns (6 * 12 - 2 = 70) or 5 columns (58) but not 7 + // columns (7 * 12 - 2 = 82). + // + // You can simulate this test by creating 19 files named "file00.txt" through "file_18.txt". completion_list_t completions; - for (size_t i=0; i < 19; i++) - { + for (size_t i = 0; i < 19; i++) { append_completion(&completions, L"abcdefghij"); } @@ -1728,41 +1471,34 @@ static void test_pager_navigation() pager.set_term_size(80, 24); page_rendering_t render = pager.render(); - if (render.term_width != 80) - err(L"Wrong term width"); - if (render.term_height != 24) - err(L"Wrong term height"); + if (render.term_width != 80) err(L"Wrong term width"); + if (render.term_height != 24) err(L"Wrong term height"); size_t rows = 4, cols = 5; - /* We have 19 completions. We can fit into 6 columns with 4 rows or 5 columns with 4 rows; the second one is better and so is what we ought to have picked. */ - if (render.rows != rows) - err(L"Wrong row count"); - if (render.cols != cols) - err(L"Wrong column count"); + // We have 19 completions. We can fit into 6 columns with 4 rows or 5 columns with 4 rows; the + // second one is better and so is what we ought to have picked. + if (render.rows != rows) err(L"Wrong row count"); + if (render.cols != cols) err(L"Wrong column count"); - /* Initially expect to have no completion index */ - if (render.selected_completion_idx != (size_t)(-1)) - { + // Initially expect to have no completion index. + if (render.selected_completion_idx != (size_t)(-1)) { err(L"Wrong initial selection"); } - /* Here are navigation directions and where we expect the selection to be */ - const struct - { + // Here are navigation directions and where we expect the selection to be. + const struct { selection_direction_t dir; size_t sel; - } - cmds[] = - { - /* Tab completion to get into the list */ + } cmds[] = { + // Tab completion to get into the list. {direction_next, 0}, - /* Westward motion in upper left wraps along the top row */ + // Westward motion in upper left wraps along the top row. {direction_west, 16}, {direction_east, 1}, - /* "Next" motion goes down the column */ + // "Next" motion goes down the column. {direction_next, 2}, {direction_next, 3}, @@ -1776,13 +1512,13 @@ static void test_pager_navigation() {direction_west, 18}, {direction_east, 3}, - /* Eastward motion wraps along the bottom, westward goes to the prior column */ + // Eastward motion wraps along the bottom, westward goes to the prior column. {direction_east, 7}, {direction_east, 11}, {direction_east, 15}, {direction_east, 3}, - /* Column memory */ + // Column memory. {direction_west, 18}, {direction_south, 15}, {direction_north, 18}, @@ -1790,7 +1526,7 @@ static void test_pager_navigation() {direction_south, 15}, {direction_north, 14}, - /* pages */ + // Pages. {direction_page_north, 12}, {direction_page_south, 15}, {direction_page_north, 12}, @@ -1802,123 +1538,104 @@ static void test_pager_navigation() {direction_page_south, 3}, }; - for (size_t i=0; i < sizeof cmds / sizeof *cmds; i++) - { + for (size_t i = 0; i < sizeof cmds / sizeof *cmds; i++) { pager.select_next_completion_in_direction(cmds[i].dir, render); pager.update_rendering(&render); - if (cmds[i].sel != render.selected_completion_idx) - { - err(L"For command %lu, expected selection %lu, but found instead %lu\n", i, cmds[i].sel, render.selected_completion_idx); + if (cmds[i].sel != render.selected_completion_idx) { + err(L"For command %lu, expected selection %lu, but found instead %lu\n", i, cmds[i].sel, + render.selected_completion_idx); } } - } -enum word_motion_t -{ - word_motion_left, - word_motion_right -}; -static void test_1_word_motion(word_motion_t motion, move_word_style_t style, const wcstring &test) -{ +enum word_motion_t { word_motion_left, word_motion_right }; +static void test_1_word_motion(word_motion_t motion, move_word_style_t style, + const wcstring &test) { wcstring command; std::set stops; - // Carets represent stops and should be cut out of the command - for (size_t i=0; i < test.size(); i++) - { + // Carets represent stops and should be cut out of the command. + for (size_t i = 0; i < test.size(); i++) { wchar_t wc = test.at(i); - if (wc == L'^') - { + if (wc == L'^') { stops.insert(command.size()); - } - else - { + } else { command.push_back(wc); } } size_t idx, end; - if (motion == word_motion_left) - { + if (motion == word_motion_left) { idx = command.size(); end = 0; - } - else - { + } else { idx = 0; end = command.size(); } move_word_state_machine_t sm(style); - while (idx != end) - { + while (idx != end) { size_t char_idx = (motion == word_motion_left ? idx - 1 : idx); wchar_t wc = command.at(char_idx); - bool will_stop = ! sm.consume_char(wc); - //printf("idx %lu, looking at %lu (%c): %d\n", idx, char_idx, (char)wc, will_stop); + bool will_stop = !sm.consume_char(wc); + // printf("idx %lu, looking at %lu (%c): %d\n", idx, char_idx, (char)wc, will_stop); bool expected_stop = (stops.count(idx) > 0); - if (will_stop != expected_stop) - { + if (will_stop != expected_stop) { wcstring tmp = command; tmp.insert(idx, L"^"); const char *dir = (motion == word_motion_left ? "left" : "right"); - if (will_stop) - { - err(L"Word motion: moving %s, unexpected stop at idx %lu: '%ls'", dir, idx, tmp.c_str()); - } - else if (! will_stop && expected_stop) - { - err(L"Word motion: moving %s, should have stopped at idx %lu: '%ls'", dir, idx, tmp.c_str()); + if (will_stop) { + err(L"Word motion: moving %s, unexpected stop at idx %lu: '%ls'", dir, idx, + tmp.c_str()); + } else if (!will_stop && expected_stop) { + err(L"Word motion: moving %s, should have stopped at idx %lu: '%ls'", dir, idx, + tmp.c_str()); } } - // We don't expect to stop here next time - if (expected_stop) - { + // We don't expect to stop here next time. + if (expected_stop) { stops.erase(idx); } - if (will_stop) - { + if (will_stop) { sm.reset(); - } - else - { + } else { idx += (motion == word_motion_left ? -1 : 1); } } } -/** Test word motion (forward-word, etc.). Carets represent cursor stops. */ -static void test_word_motion() -{ +/// Test word motion (forward-word, etc.). Carets represent cursor stops. +static void test_word_motion() { say(L"Testing word motion"); test_1_word_motion(word_motion_left, move_word_style_punctuation, L"^echo ^hello_^world.^txt"); test_1_word_motion(word_motion_right, move_word_style_punctuation, L"echo^ hello^_world^.txt^"); - test_1_word_motion(word_motion_left, move_word_style_punctuation, L"echo ^foo_^foo_^foo/^/^/^/^/^ "); - test_1_word_motion(word_motion_right, move_word_style_punctuation, L"echo^ foo^_foo^_foo^/^/^/^/^/ ^"); + test_1_word_motion(word_motion_left, move_word_style_punctuation, + L"echo ^foo_^foo_^foo/^/^/^/^/^ "); + test_1_word_motion(word_motion_right, move_word_style_punctuation, + L"echo^ foo^_foo^_foo^/^/^/^/^/ ^"); test_1_word_motion(word_motion_left, move_word_style_path_components, L"^/^foo/^bar/^baz/"); test_1_word_motion(word_motion_left, move_word_style_path_components, L"^echo ^--foo ^--bar"); - test_1_word_motion(word_motion_left, move_word_style_path_components, L"^echo ^hi ^> /^dev/^null"); + test_1_word_motion(word_motion_left, move_word_style_path_components, + L"^echo ^hi ^> /^dev/^null"); - test_1_word_motion(word_motion_left, move_word_style_path_components, L"^echo /^foo/^bar{^aaa,^bbb,^ccc}^bak/"); + test_1_word_motion(word_motion_left, move_word_style_path_components, + L"^echo /^foo/^bar{^aaa,^bbb,^ccc}^bak/"); } -/** Test is_potential_path */ -static void test_is_potential_path() -{ +/// Test is_potential_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/"); } - /* Directories */ + // Directories if (system("mkdir -p /tmp/is_potential_path_test/alpha/")) err(L"mkdir failed"); if (system("mkdir -p /tmp/is_potential_path_test/beta/")) err(L"mkdir failed"); - /* Files */ + // Files if (system("touch /tmp/is_potential_path_test/aardvark")) err(L"touch failed"); if (system("touch /tmp/is_potential_path_test/gamma")) err(L"touch failed"); @@ -1929,49 +1646,44 @@ static void test_is_potential_path() do_test(is_potential_path(L"alpha/", wds, PATH_REQUIRE_DIR)); do_test(is_potential_path(L"aard", wds, 0)); - do_test(! is_potential_path(L"balpha/", wds, PATH_REQUIRE_DIR)); - do_test(! is_potential_path(L"aard", wds, PATH_REQUIRE_DIR)); - do_test(! is_potential_path(L"aarde", wds, PATH_REQUIRE_DIR)); - do_test(! is_potential_path(L"aarde", wds, 0)); + do_test(!is_potential_path(L"balpha/", wds, PATH_REQUIRE_DIR)); + do_test(!is_potential_path(L"aard", wds, PATH_REQUIRE_DIR)); + do_test(!is_potential_path(L"aarde", wds, PATH_REQUIRE_DIR)); + do_test(!is_potential_path(L"aarde", wds, 0)); do_test(is_potential_path(L"/tmp/is_potential_path_test/aardvark", wds, 0)); do_test(is_potential_path(L"/tmp/is_potential_path_test/al", wds, PATH_REQUIRE_DIR)); do_test(is_potential_path(L"/tmp/is_potential_path_test/aardv", wds, 0)); - do_test(! is_potential_path(L"/tmp/is_potential_path_test/aardvark", wds, PATH_REQUIRE_DIR)); - do_test(! is_potential_path(L"/tmp/is_potential_path_test/al/", wds, 0)); - do_test(! is_potential_path(L"/tmp/is_potential_path_test/ar", wds, 0)); + do_test(!is_potential_path(L"/tmp/is_potential_path_test/aardvark", wds, PATH_REQUIRE_DIR)); + do_test(!is_potential_path(L"/tmp/is_potential_path_test/al/", wds, 0)); + do_test(!is_potential_path(L"/tmp/is_potential_path_test/ar", wds, 0)); do_test(is_potential_path(L"/usr", wds, PATH_REQUIRE_DIR)); - } -/** Test the 'test' builtin */ +/// Test the 'test' builtin. int builtin_test(parser_t &parser, io_streams_t &streams, wchar_t **argv); -static bool run_one_test_test(int expected, wcstring_list_t &lst, bool bracket) -{ +static bool run_one_test_test(int expected, wcstring_list_t &lst, bool bracket) { parser_t parser; size_t i, count = lst.size(); - wchar_t **argv = new wchar_t *[count+3]; + wchar_t **argv = new wchar_t *[count + 3]; argv[0] = (wchar_t *)(bracket ? L"[" : L"test"); - for (i=0; i < count; i++) - { - argv[i+1] = (wchar_t *)lst.at(i).c_str(); + for (i = 0; i < count; i++) { + argv[i + 1] = (wchar_t *)lst.at(i).c_str(); } - if (bracket) - { - argv[i+1] = (wchar_t *)L"]"; + if (bracket) { + argv[i + 1] = (wchar_t *)L"]"; i++; } - argv[i+1] = NULL; + argv[i + 1] = NULL; io_streams_t streams; int result = builtin_test(parser, streams, argv); delete[] argv; 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; @@ -1986,9 +1698,8 @@ static bool run_test_test(int expected, const wcstring &str) return nonbracket; } -static void test_test_brackets() -{ - // Ensure [ knows it needs a ] +static void test_test_brackets() { + // Ensure [ knows it needs a ]. parser_t parser; io_streams_t streams; @@ -2000,11 +1711,9 @@ static void test_test_brackets() const wchar_t *argv3[] = {L"[", L"foo", L"]", L"bar", NULL}; do_test(builtin_test(parser, streams, (wchar_t **)argv3) != 0); - } -static void test_test() -{ +static void test_test() { say(L"Testing test builtin"); test_test_brackets(); @@ -2031,19 +1740,19 @@ static void test_test() do_test(run_test_test(0, L"-n 5 -a 10 -gt 5")); do_test(run_test_test(0, L"-n 3 -a -n 5")); - /* test precedence: - '0 == 0 || 0 == 1 && 0 == 2' - should be evaluated as: - '0 == 0 || (0 == 1 && 0 == 2)' - and therefore true. If it were - '(0 == 0 || 0 == 1) && 0 == 2' - it would be false. */ + // Test precedence: + // '0 == 0 || 0 == 1 && 0 == 2' + // should be evaluated as: + // '0 == 0 || (0 == 1 && 0 == 2)' + // and therefore true. If it were + // '(0 == 0 || 0 == 1) && 0 == 2' + // it would be false. do_test(run_test_test(0, L"0 = 0 -o 0 = 1 -a 0 = 2")); do_test(run_test_test(0, L"-n 5 -o 0 = 1 -a 0 = 2")); do_test(run_test_test(1, L"( 0 = 0 -o 0 = 1 ) -a 0 = 2")); do_test(run_test_test(0, L"0 = 0 -o ( 0 = 1 -a 0 = 2 )")); - /* A few lame tests for permissions; these need to be a lot more complete. */ + // A few lame tests for permissions; these need to be a lot more complete. do_test(run_test_test(0, L"-e /bin/ls")); do_test(run_test_test(1, L"-e /bin/ls_not_a_path")); do_test(run_test_test(0, L"-x /bin/ls")); @@ -2051,30 +1760,30 @@ static void test_test() do_test(run_test_test(0, L"-d /bin/")); do_test(run_test_test(1, L"-d /bin/ls")); - /* This failed at one point */ + // This failed at one point. do_test(run_test_test(1, L"-d /bin -a 5 -eq 3")); do_test(run_test_test(0, L"-d /bin -o 5 -eq 3")); do_test(run_test_test(0, L"-d /bin -a ! 5 -eq 3")); - /* We didn't properly handle multiple "just strings" either */ + // We didn't properly handle multiple "just strings" either. do_test(run_test_test(0, L"foo")); do_test(run_test_test(0, L"foo -a bar")); - /* These should be errors */ + // These should be errors. do_test(run_test_test(1, L"foo bar")); do_test(run_test_test(1, L"foo bar baz")); - /* This crashed */ + // This crashed. do_test(run_test_test(1, L"1 = 1 -a = 1")); - /* Make sure we can treat -S as a parameter instead of an operator. https://github.com/fish-shell/fish-shell/issues/601 */ + // Make sure we can treat -S as a parameter instead of an operator. + // https://github.com/fish-shell/fish-shell/issues/601 do_test(run_test_test(0, L"-S = -S")); do_test(run_test_test(1, L"! ! ! A")); } -/** Testing colors */ -static void test_colors() -{ +/// Testing colors. +static void test_colors() { say(L"Testing colors"); do_test(rgb_color_t(L"#FF00A0").is_rgb()); do_test(rgb_color_t(L"FF00A0").is_rgb()); @@ -2089,8 +1798,7 @@ static void test_colors() do_test(rgb_color_t(L"mooganta").is_none()); } -static void test_complete(void) -{ +static void test_complete(void) { say(L"Testing complete"); const wchar_t *name_strs[] = {L"Foo1", L"Foo2", L"Foo3", L"Bar1", L"Bar2", L"Bar3"}; @@ -2098,7 +1806,7 @@ static void test_complete(void) const wcstring_list_t names(name_strs, name_strs + count); complete_set_variable_names(&names); - + const env_vars_snapshot_t &vars = env_vars_snapshot_t::current(); std::vector completions; @@ -2126,7 +1834,8 @@ static void test_complete(void) do_test(completions.empty()); completions.clear(); - complete(L"$1", &completions, COMPLETION_REQUEST_DEFAULT | COMPLETION_REQUEST_FUZZY_MATCH, vars); + complete(L"$1", &completions, COMPLETION_REQUEST_DEFAULT | COMPLETION_REQUEST_FUZZY_MATCH, + vars); completions_sort_and_prioritize(&completions); do_test(completions.size() == 2); do_test(completions.at(0).completion == L"$Bar1"); @@ -2142,50 +1851,53 @@ static void test_complete(void) do_test(completions.at(0).completion == L"e"); completions.clear(); - complete(L"echo (ls /tmp/complete_test/testfil", &completions, COMPLETION_REQUEST_DEFAULT, vars); + complete(L"echo (ls /tmp/complete_test/testfil", &completions, COMPLETION_REQUEST_DEFAULT, + vars); do_test(completions.size() == 1); do_test(completions.at(0).completion == L"e"); completions.clear(); - complete(L"echo (command ls /tmp/complete_test/testfil", &completions, COMPLETION_REQUEST_DEFAULT, vars); + complete(L"echo (command ls /tmp/complete_test/testfil", &completions, + COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.size() == 1); do_test(completions.at(0).completion == L"e"); - /* Add a function and test completing it in various ways */ + // Add a function and test completing it in various ways. struct function_data_t func_data = {}; func_data.name = L"scuttlebutt"; func_data.definition = L"echo gongoozle"; function_add(func_data, parser_t::principal_parser()); - /* Complete a function name */ + // Complete a function name. completions.clear(); complete(L"echo (scuttlebut", &completions, COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.size() == 1); do_test(completions.at(0).completion == L"t"); - /* But not with the command prefix */ + // But not with the command prefix. completions.clear(); complete(L"echo (command scuttlebut", &completions, COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.size() == 0); - /* Not with the builtin prefix */ + // Not with the builtin prefix. completions.clear(); complete(L"echo (builtin scuttlebut", &completions, COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.size() == 0); - /* Not after a redirection */ + // Not after a redirection. completions.clear(); complete(L"echo hi > scuttlebut", &completions, COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.size() == 0); - /* Trailing spaces (#1261) */ - complete_add(L"foobarbaz", false, wcstring(), option_type_args_only, NO_FILES, NULL, L"qux", NULL, COMPLETE_AUTO_SPACE); + // Trailing spaces (#1261). + complete_add(L"foobarbaz", false, wcstring(), option_type_args_only, NO_FILES, NULL, L"qux", + NULL, COMPLETE_AUTO_SPACE); completions.clear(); complete(L"foobarbaz ", &completions, COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.size() == 1); do_test(completions.at(0).completion == L"qux"); - /* Don't complete variable names in single quotes (#1023) */ + // Don't complete variable names in single quotes (#1023). completions.clear(); complete(L"echo '$Foo", &completions, COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.empty()); @@ -2193,15 +1905,14 @@ static void test_complete(void) complete(L"echo \\$Foo", &completions, COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.empty()); - /* File completions */ + // File completions. char saved_wd[PATH_MAX + 1] = {}; - if (!getcwd(saved_wd, sizeof saved_wd)) - { + if (!getcwd(saved_wd, sizeof saved_wd)) { perror("getcwd"); exit(-1); } if (chdir_set_pwd("/tmp/complete_test/")) err(L"chdir failed"); - + complete(L"cat te", &completions, COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.size() == 1); do_test(completions.at(0).completion == L"stfile"); @@ -2239,7 +1950,7 @@ static void test_complete(void) do_test(completions.at(0).completion == L"stfile"); completions.clear(); - // Zero escapes can cause problems. See #1631 + // Zero escapes can cause problems. See issue #1631. complete(L"cat foo\\0", &completions, COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.empty()); completions.clear(); @@ -2258,23 +1969,22 @@ static void test_complete(void) complete_set_variable_names(NULL); - /* Test wraps */ + // Test wraps. do_test(comma_join(complete_get_wrap_chain(L"wrapper1")) == L"wrapper1"); complete_add_wrapper(L"wrapper1", L"wrapper2"); do_test(comma_join(complete_get_wrap_chain(L"wrapper1")) == L"wrapper1,wrapper2"); complete_add_wrapper(L"wrapper2", L"wrapper3"); do_test(comma_join(complete_get_wrap_chain(L"wrapper1")) == L"wrapper1,wrapper2,wrapper3"); - complete_add_wrapper(L"wrapper3", L"wrapper1"); //loop! + complete_add_wrapper(L"wrapper3", L"wrapper1"); // loop! do_test(comma_join(complete_get_wrap_chain(L"wrapper1")) == L"wrapper1,wrapper2,wrapper3"); complete_remove_wrapper(L"wrapper1", L"wrapper2"); do_test(comma_join(complete_get_wrap_chain(L"wrapper1")) == L"wrapper1"); do_test(comma_join(complete_get_wrap_chain(L"wrapper2")) == L"wrapper2,wrapper3,wrapper1"); } -static void test_1_completion(wcstring line, const wcstring &completion, complete_flags_t flags, bool append_only, wcstring expected, long source_line) -{ - // str is given with a caret, which we use to represent the cursor position - // find it +static void test_1_completion(wcstring line, const wcstring &completion, complete_flags_t flags, + bool append_only, wcstring expected, long source_line) { + // str is given with a caret, which we use to represent the cursor position. Find it. const size_t in_cursor_pos = line.find(L'^'); do_test(in_cursor_pos != wcstring::npos); line.erase(in_cursor_pos, 1); @@ -2284,27 +1994,28 @@ static void test_1_completion(wcstring line, const wcstring &completion, complet expected.erase(out_cursor_pos, 1); size_t cursor_pos = in_cursor_pos; - wcstring result = completion_apply_to_command_line(completion, flags, line, &cursor_pos, append_only); - if (result != expected) - { - fprintf(stderr, "line %ld: %ls + %ls -> [%ls], expected [%ls]\n", source_line, line.c_str(), completion.c_str(), result.c_str(), expected.c_str()); + wcstring result = + completion_apply_to_command_line(completion, flags, line, &cursor_pos, append_only); + if (result != expected) { + fprintf(stderr, "line %ld: %ls + %ls -> [%ls], expected [%ls]\n", source_line, line.c_str(), + completion.c_str(), result.c_str(), expected.c_str()); } do_test(result == expected); do_test(cursor_pos == out_cursor_pos); } -static void test_completion_insertions() -{ +static void test_completion_insertions() { #define TEST_1_COMPLETION(a, b, c, d, e) test_1_completion(a, b, c, d, e, __LINE__) say(L"Testing completion insertions"); TEST_1_COMPLETION(L"foo^", L"bar", 0, false, L"foobar ^"); - TEST_1_COMPLETION(L"foo^ baz", L"bar", 0, false, L"foobar ^ baz"); //we really do want to insert two spaces here - otherwise it's hidden by the cursor + // We really do want to insert two spaces here - otherwise it's hidden by the cursor. + TEST_1_COMPLETION(L"foo^ baz", L"bar", 0, false, L"foobar ^ baz"); TEST_1_COMPLETION(L"'foo^", L"bar", 0, false, L"'foobar' ^"); TEST_1_COMPLETION(L"'foo'^", L"bar", 0, false, L"'foobar' ^"); TEST_1_COMPLETION(L"'foo\\'^", L"bar", 0, false, L"'foo\\'bar' ^"); TEST_1_COMPLETION(L"foo\\'^", L"bar", 0, false, L"foo\\'bar ^"); - // Test append only + // Test append only. TEST_1_COMPLETION(L"foo^", L"bar", 0, true, L"foobar ^"); TEST_1_COMPLETION(L"foo^ baz", L"bar", 0, true, L"foobar ^ baz"); TEST_1_COMPLETION(L"'foo^", L"bar", 0, true, L"'foobar' ^"); @@ -2322,33 +2033,35 @@ static void test_completion_insertions() TEST_1_COMPLETION(L"'foo^", L"bar", COMPLETE_REPLACES_TOKEN, false, L"bar ^"); } -static void perform_one_autosuggestion_cd_test(const wcstring &command, const env_vars_snapshot_t &vars, const wcstring &expected, long line) -{ +static void perform_one_autosuggestion_cd_test(const wcstring &command, + const env_vars_snapshot_t &vars, + const wcstring &expected, long line) { std::vector comps; - complete(command, &comps,COMPLETION_REQUEST_AUTOSUGGESTION, vars); - + complete(command, &comps, COMPLETION_REQUEST_AUTOSUGGESTION, vars); + bool expects_error = (expected == L""); - - if (comps.empty() && ! expects_error) - { - printf("line %ld: autosuggest_suggest_special() failed for command %ls\n", line, command.c_str()); - do_test(! comps.empty()); + + if (comps.empty() && !expects_error) { + printf("line %ld: autosuggest_suggest_special() failed for command %ls\n", line, + command.c_str()); + do_test(!comps.empty()); return; - } - else if (! comps.empty() && expects_error) - { - printf("line %ld: autosuggest_suggest_special() was expected to fail but did not, for command %ls\n", line, command.c_str()); + } else if (!comps.empty() && expects_error) { + printf( + "line %ld: autosuggest_suggest_special() was expected to fail but did not, for command " + "%ls\n", + line, command.c_str()); do_test(comps.empty()); } - - if (! comps.empty()) - { + + if (!comps.empty()) { completions_sort_and_prioritize(&comps); const completion_t &suggestion = comps.at(0); - - if (suggestion.completion != expected) - { - printf("line %ld: complete() for cd returned the wrong expected string for command %ls\n", line, command.c_str()); + + if (suggestion.completion != expected) { + printf( + "line %ld: complete() for cd returned the wrong expected string for command %ls\n", + line, command.c_str()); printf(" actual: %ls\n", suggestion.completion.c_str()); printf("expected: %ls\n", expected.c_str()); do_test(suggestion.completion == expected); @@ -2356,29 +2069,33 @@ static void perform_one_autosuggestion_cd_test(const wcstring &command, const en } } - -/* Testing test_autosuggest_suggest_special, in particular for properly handling quotes and backslashes */ -static void test_autosuggest_suggest_special() -{ +// Testing test_autosuggest_suggest_special, in particular for properly handling quotes and +// backslashes. +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"); if (system("mkdir -p '/tmp/autosuggest_test/3foo\\bar'")) err(L"mkdir failed"); - if (system("mkdir -p /tmp/autosuggest_test/4foo\\'bar")) err(L"mkdir failed"); //a path with a single quote - if (system("mkdir -p /tmp/autosuggest_test/5foo\\\"bar")) err(L"mkdir failed"); //a path with a double quote - if (system("mkdir -p ~/test_autosuggest_suggest_special/")) err(L"mkdir failed"); //make sure tilde is handled + if (system("mkdir -p /tmp/autosuggest_test/4foo\\'bar")) + err(L"mkdir failed"); // a path with a single quote + if (system("mkdir -p /tmp/autosuggest_test/5foo\\\"bar")) + err(L"mkdir failed"); // a path with a double quote + if (system("mkdir -p ~/test_autosuggest_suggest_special/")) + err(L"mkdir failed"); // make sure tilde is handled if (system("mkdir -p /tmp/autosuggest_test/start/unique2/unique3/multi4")) err(L"mkdir failed"); - if (system("mkdir -p /tmp/autosuggest_test/start/unique2/unique3/multi42")) err(L"mkdir failed"); - if (system("mkdir -p /tmp/autosuggest_test/start/unique2/.hiddenDir/moreStuff")) err(L"mkdir failed"); - + if (system("mkdir -p /tmp/autosuggest_test/start/unique2/unique3/multi42")) + err(L"mkdir failed"); + if (system("mkdir -p /tmp/autosuggest_test/start/unique2/.hiddenDir/moreStuff")) + err(L"mkdir failed"); + char saved_wd[PATH_MAX] = {}; if (NULL == getcwd(saved_wd, sizeof saved_wd)) err(L"getcwd failed"); - + const wcstring wd = L"/tmp/autosuggest_test/"; if (chdir_set_pwd(wcs2string(wd).c_str())) err(L"chdir failed"); - + env_set(L"AUTOSUGGEST_TEST_LOC", wd.c_str(), ENV_LOCAL); - + const env_vars_snapshot_t &vars = env_vars_snapshot_t::current(); perform_one_autosuggestion_cd_test(L"cd /tmp/autosuggest_test/0", vars, L"foobar/", __LINE__); @@ -2389,54 +2106,64 @@ static void test_autosuggest_suggest_special() perform_one_autosuggestion_cd_test(L"cd '0", vars, L"foobar/", __LINE__); perform_one_autosuggestion_cd_test(L"cd /tmp/autosuggest_test/1", vars, L"foo bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd \"/tmp/autosuggest_test/1", vars, L"foo bar/", __LINE__); + perform_one_autosuggestion_cd_test(L"cd \"/tmp/autosuggest_test/1", vars, L"foo bar/", + __LINE__); perform_one_autosuggestion_cd_test(L"cd '/tmp/autosuggest_test/1", vars, L"foo bar/", __LINE__); perform_one_autosuggestion_cd_test(L"cd 1", vars, L"foo bar/", __LINE__); perform_one_autosuggestion_cd_test(L"cd \"1", vars, L"foo bar/", __LINE__); perform_one_autosuggestion_cd_test(L"cd '1", vars, L"foo bar/", __LINE__); perform_one_autosuggestion_cd_test(L"cd /tmp/autosuggest_test/2", vars, L"foo bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd \"/tmp/autosuggest_test/2", vars, L"foo bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd '/tmp/autosuggest_test/2", vars, L"foo bar/", __LINE__); + perform_one_autosuggestion_cd_test(L"cd \"/tmp/autosuggest_test/2", vars, L"foo bar/", + __LINE__); + perform_one_autosuggestion_cd_test(L"cd '/tmp/autosuggest_test/2", vars, L"foo bar/", + __LINE__); perform_one_autosuggestion_cd_test(L"cd 2", vars, L"foo bar/", __LINE__); perform_one_autosuggestion_cd_test(L"cd \"2", vars, L"foo bar/", __LINE__); perform_one_autosuggestion_cd_test(L"cd '2", vars, L"foo bar/", __LINE__); perform_one_autosuggestion_cd_test(L"cd /tmp/autosuggest_test/3", vars, L"foo\\bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd \"/tmp/autosuggest_test/3", vars, L"foo\\bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd '/tmp/autosuggest_test/3", vars, L"foo\\bar/", __LINE__); + perform_one_autosuggestion_cd_test(L"cd \"/tmp/autosuggest_test/3", vars, L"foo\\bar/", + __LINE__); + perform_one_autosuggestion_cd_test(L"cd '/tmp/autosuggest_test/3", vars, L"foo\\bar/", + __LINE__); perform_one_autosuggestion_cd_test(L"cd 3", vars, L"foo\\bar/", __LINE__); perform_one_autosuggestion_cd_test(L"cd \"3", vars, L"foo\\bar/", __LINE__); perform_one_autosuggestion_cd_test(L"cd '3", vars, L"foo\\bar/", __LINE__); perform_one_autosuggestion_cd_test(L"cd /tmp/autosuggest_test/4", vars, L"foo'bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd \"/tmp/autosuggest_test/4", vars, L"foo'bar/", __LINE__); + perform_one_autosuggestion_cd_test(L"cd \"/tmp/autosuggest_test/4", vars, L"foo'bar/", + __LINE__); perform_one_autosuggestion_cd_test(L"cd '/tmp/autosuggest_test/4", vars, L"foo'bar/", __LINE__); perform_one_autosuggestion_cd_test(L"cd 4", vars, L"foo'bar/", __LINE__); perform_one_autosuggestion_cd_test(L"cd \"4", vars, L"foo'bar/", __LINE__); perform_one_autosuggestion_cd_test(L"cd '4", vars, L"foo'bar/", __LINE__); perform_one_autosuggestion_cd_test(L"cd /tmp/autosuggest_test/5", vars, L"foo\"bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd \"/tmp/autosuggest_test/5", vars, L"foo\"bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd '/tmp/autosuggest_test/5", vars, L"foo\"bar/", __LINE__); + perform_one_autosuggestion_cd_test(L"cd \"/tmp/autosuggest_test/5", vars, L"foo\"bar/", + __LINE__); + perform_one_autosuggestion_cd_test(L"cd '/tmp/autosuggest_test/5", vars, L"foo\"bar/", + __LINE__); perform_one_autosuggestion_cd_test(L"cd 5", vars, L"foo\"bar/", __LINE__); perform_one_autosuggestion_cd_test(L"cd \"5", vars, L"foo\"bar/", __LINE__); perform_one_autosuggestion_cd_test(L"cd '5", vars, L"foo\"bar/", __LINE__); - - perform_one_autosuggestion_cd_test(L"cd $AUTOSUGGEST_TEST_LOC/0", vars, L"foobar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd ~/test_autosuggest_suggest_specia", vars, L"l/", __LINE__); - - perform_one_autosuggestion_cd_test(L"cd /tmp/autosuggest_test/start/", vars, L"unique2/unique3/", __LINE__); - // A single quote should defeat tilde expansion - perform_one_autosuggestion_cd_test(L"cd '~/test_autosuggest_suggest_specia'", vars, L"", __LINE__); - - // Don't crash on ~ (2696) - // note this was wd dependent, hence why we set it + perform_one_autosuggestion_cd_test(L"cd $AUTOSUGGEST_TEST_LOC/0", vars, L"foobar/", __LINE__); + perform_one_autosuggestion_cd_test(L"cd ~/test_autosuggest_suggest_specia", vars, L"l/", + __LINE__); + + perform_one_autosuggestion_cd_test(L"cd /tmp/autosuggest_test/start/", vars, + L"unique2/unique3/", __LINE__); + + // A single quote should defeat tilde expansion. + perform_one_autosuggestion_cd_test(L"cd '~/test_autosuggest_suggest_specia'", vars, L"", + __LINE__); + + // Don't crash on ~ (issue #2696). Note this was wd dependent, hence why we set it. if (chdir_set_pwd("/tmp/autosuggest_test/")) err(L"chdir failed"); - + if (system("mkdir -p '/tmp/autosuggest_test/~hahaha/path1/path2/'")) err(L"mkdir failed"); - + perform_one_autosuggestion_cd_test(L"cd ~haha", vars, L"ha/path1/path2/", __LINE__); perform_one_autosuggestion_cd_test(L"cd ~hahaha/", vars, L"path1/path2/", __LINE__); if (chdir_set_pwd(saved_wd)) err(L"chdir failed"); @@ -2445,65 +2172,53 @@ static void test_autosuggest_suggest_special() if (system("rm -Rf ~/test_autosuggest_suggest_special/")) err(L"rm failed"); } -static void perform_one_autosuggestion_should_ignore_test(const wcstring &command, const wcstring &wd, long line) -{ +static void perform_one_autosuggestion_should_ignore_test(const wcstring &command, + const wcstring &wd, long line) { completion_list_t comps; complete(command, &comps, COMPLETION_REQUEST_AUTOSUGGESTION, env_vars_snapshot_t::current()); do_test(comps.empty()); - if (! comps.empty()) - { + if (!comps.empty()) { const wcstring &suggestion = comps.front().completion; printf("line %ld: complete() expected to return nothing for %ls\n", line, command.c_str()); printf(" instead got: %ls\n", suggestion.c_str()); } } -static void test_autosuggestion_ignores() -{ +static void test_autosuggestion_ignores() { say(L"Testing scenarios that should produce no autosuggestions"); const wcstring wd = L"/tmp/autosuggest_test/"; - // Do not do file autosuggestions immediately after certain statement terminators - see #1631 + // Do not do file autosuggestions immediately after certain statement terminators - see #1631. perform_one_autosuggestion_should_ignore_test(L"echo PIPE_TEST|", wd, __LINE__); perform_one_autosuggestion_should_ignore_test(L"echo PIPE_TEST&", wd, __LINE__); perform_one_autosuggestion_should_ignore_test(L"echo PIPE_TEST#comment", wd, __LINE__); perform_one_autosuggestion_should_ignore_test(L"echo PIPE_TEST;", wd, __LINE__); } -static void test_autosuggestion_combining() -{ +static void test_autosuggestion_combining() { say(L"Testing autosuggestion combining"); do_test(combine_command_and_autosuggestion(L"alpha", L"alphabeta") == L"alphabeta"); - // when the last token contains no capital letters, we use the case of the autosuggestion + // When the last token contains no capital letters, we use the case of the autosuggestion. do_test(combine_command_and_autosuggestion(L"alpha", L"ALPHABETA") == L"ALPHABETA"); - // when the last token contains capital letters, we use its case + // When the last token contains capital letters, we use its case. do_test(combine_command_and_autosuggestion(L"alPha", L"alphabeTa") == L"alPhabeTa"); - // if autosuggestion is not longer than input, use the input's case + // If autosuggestion is not longer than input, use the input's case. do_test(combine_command_and_autosuggestion(L"alpha", L"ALPHAA") == L"ALPHAA"); do_test(combine_command_and_autosuggestion(L"alpha", L"ALPHA") == L"alpha"); } - -/** - Test speed of completion calculations -*/ -void perf_complete() -{ +/// Test speed of completion calculations. +void perf_complete() { wchar_t c; std::vector out; long long t1, t2; - int matches=0; + int matches = 0; double t; - wchar_t str[3]= - { - 0, 0, 0 - } - ; + wchar_t str[3] = {0, 0, 0}; int i; - say(L"Testing completion performance"); reader_push(L""); @@ -2511,10 +2226,8 @@ void perf_complete() t1 = get_time(); - - for (c=L'a'; c<=L'z'; c++) - { - str[0]=c; + for (c = L'a'; c <= L'z'; c++) { + str[0] = c; reader_set_buffer(str, 0); complete(str, &out, COMPLETION_REQUEST_DEFAULT, env_vars_snapshot_t::current()); @@ -2522,18 +2235,18 @@ void perf_complete() 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; + matches = 0; t1 = get_time(); - for (i=0; iitem_at_index(i); - if (item.empty()) - break; + if (item.empty()) break; - if (item.str() == txt) - { + if (item.str() == txt) { result = true; break; } @@ -2588,24 +2294,24 @@ static bool history_contains(history_t *history, const wcstring &txt) return result; } -static void test_input() -{ +static void test_input() { say(L"Testing input"); - /* Ensure sequences are order independent. Here we add two bindings where the first is a prefix of the second, and then emit the second key list. The second binding should be invoked, not the first! */ + // Ensure sequences are order independent. Here we add two bindings where the first is a prefix + // of the second, and then emit the second key list. The second binding should be invoked, not + // the first! wcstring prefix_binding = L"qqqqqqqa"; wcstring desired_binding = prefix_binding + L'a'; input_mapping_add(prefix_binding.c_str(), L"up-line"); input_mapping_add(desired_binding.c_str(), L"down-line"); - /* Push the desired binding to the queue */ + // Push the desired binding to the queue. for (size_t idx = 0; idx < desired_binding.size(); idx++) { input_queue_ch(desired_binding.at(idx)); } - /* Now test */ + // Now test. wint_t c = input_readch(); - if (c != R_DOWN_LINE) - { + if (c != R_DOWN_LINE) { err(L"Expected to read char R_DOWN_LINE, but instead got %ls\n", describe_char(c).c_str()); } } @@ -2613,43 +2319,36 @@ static void test_input() #define UVARS_PER_THREAD 8 #define UVARS_TEST_PATH L"/tmp/fish_uvars_test/varsfile.txt" -static int test_universal_helper(int *x) -{ +static int test_universal_helper(int *x) { env_universal_t uvars(UVARS_TEST_PATH); - for (int j=0; j < UVARS_PER_THREAD; j++) - { + for (int j = 0; j < UVARS_PER_THREAD; j++) { const wcstring key = format_string(L"key_%d_%d", *x, j); const wcstring val = format_string(L"val_%d_%d", *x, j); uvars.set(key, val, false); bool synced = uvars.sync(NULL); - if (! synced) - { + if (!synced) { err(L"Failed to sync universal variables after modification"); } fputc('.', stderr); } - /* Last step is to delete the first key */ + // Last step is to delete the first key. uvars.remove(format_string(L"key_%d_%d", *x, 0)); bool synced = uvars.sync(NULL); - if (! synced) - { + if (!synced) { err(L"Failed to sync universal variables after deletion"); } fputc('.', stderr); - return 0; } -static void test_universal() -{ +static void test_universal() { say(L"Testing universal variables"); if (system("mkdir -p /tmp/fish_uvars_test/")) err(L"mkdir failed"); const int threads = 16; static int ctx[threads]; - for (int i=0; i < threads; i++) - { + for (int i = 0; i < threads; i++) { ctx[i] = i; iothread_perform(test_universal_helper, &ctx[i]); } @@ -2657,33 +2356,27 @@ static void test_universal() env_universal_t uvars(UVARS_TEST_PATH); bool loaded = uvars.load(); - if (! loaded) - { + if (!loaded) { err(L"Failed to load universal variables"); } - for (int i=0; i < threads; i++) - { - for (int j=0; j < UVARS_PER_THREAD; j++) - { + for (int i = 0; i < threads; i++) { + for (int j = 0; j < UVARS_PER_THREAD; j++) { const wcstring key = format_string(L"key_%d_%d", i, j); env_var_t expected_val; - if (j == 0) - { + if (j == 0) { expected_val = env_var_t::missing_var(); - } - else - { + } else { expected_val = format_string(L"val_%d_%d", i, j); } const env_var_t var = uvars.get(key); - if (j == 0) - { + if (j == 0) { assert(expected_val.missing()); } - if (var != expected_val) - { + if (var != expected_val) { const wchar_t *missing_desc = L""; - err(L"Wrong value for key %ls: expected %ls, got %ls\n", key.c_str(), (expected_val.missing() ? missing_desc : expected_val.c_str()), (var.missing() ? missing_desc : var.c_str())); + err(L"Wrong value for key %ls: expected %ls, got %ls\n", key.c_str(), + (expected_val.missing() ? missing_desc : expected_val.c_str()), + (var.missing() ? missing_desc : var.c_str())); } } } @@ -2696,14 +2389,13 @@ static bool callback_data_less_than(const callback_data_t &a, const callback_dat return a.key < b.key; } -static void test_universal_callbacks() -{ +static void test_universal_callbacks() { say(L"Testing universal callbacks"); if (system("mkdir -p /tmp/fish_uvars_test/")) err(L"mkdir failed"); env_universal_t uvars1(UVARS_TEST_PATH); env_universal_t uvars2(UVARS_TEST_PATH); - /* Put some variables into both */ + // Put some variables into both. uvars1.set(L"alpha", L"1", false); uvars1.set(L"beta", L"1", false); uvars1.set(L"delta", L"1", false); @@ -2715,25 +2407,25 @@ static void test_universal_callbacks() uvars1.sync(NULL); uvars2.sync(NULL); - /* Change uvars1 */ - uvars1.set(L"alpha", L"2", false); //changes value - uvars1.set(L"beta", L"1", true); //changes export - uvars1.remove(L"delta"); //erases value - uvars1.set(L"epsilon", L"1", false); //changes nothing + // Change uvars1. + uvars1.set(L"alpha", L"2", false); // changes value + uvars1.set(L"beta", L"1", true); // changes export + uvars1.remove(L"delta"); // erases value + uvars1.set(L"epsilon", L"1", false); // changes nothing uvars1.sync(NULL); - /* Change uvars2. It should treat its value as correct and ignore changes from uvars1. */ - uvars2.set(L"lambda", L"1", false); //same value - uvars2.set(L"kappa", L"2", false); //different value + // Change uvars2. It should treat its value as correct and ignore changes from uvars1. + uvars2.set(L"lambda", L"1", false); // same value + uvars2.set(L"kappa", L"2", false); // different value - /* Now see what uvars2 sees */ + // Now see what uvars2 sees. callback_data_list_t callbacks; uvars2.sync(&callbacks); - /* Sort them to get them in a predictable order */ + // Sort them to get them in a predictable order. std::sort(callbacks.begin(), callbacks.end(), callback_data_less_than); - /* Should see exactly two changes */ + // Should see exactly two changes. do_test(callbacks.size() == 3); do_test(callbacks.at(0).type == SET); do_test(callbacks.at(0).key == L"alpha"); @@ -2745,135 +2437,121 @@ static void test_universal_callbacks() do_test(callbacks.at(2).key == L"delta"); do_test(callbacks.at(2).val == L""); - if (system("rm -Rf /tmp/fish_uvars_test")) err(L"rm failed"); } -bool poll_notifier(universal_notifier_t *note) -{ +bool poll_notifier(universal_notifier_t *note) { bool result = false; - if (note->usec_delay_between_polls() > 0) - { + if (note->usec_delay_between_polls() > 0) { result = note->poll(); } int fd = note->notification_fd(); - if (! result && fd >= 0) - { + if (!result && fd >= 0) { fd_set fds; FD_ZERO(&fds); FD_SET(fd, &fds); struct timeval tv = {0, 0}; - if (select(fd + 1, &fds, NULL, NULL, &tv) > 0 && FD_ISSET(fd, &fds)) - { + if (select(fd + 1, &fds, NULL, NULL, &tv) > 0 && FD_ISSET(fd, &fds)) { result = note->notification_fd_became_readable(fd); } } return result; } -static void trigger_or_wait_for_notification(universal_notifier_t *notifier, universal_notifier_t::notifier_strategy_t strategy) -{ - switch (strategy) - { - case universal_notifier_t::strategy_default: +static void trigger_or_wait_for_notification(universal_notifier_t *notifier, + universal_notifier_t::notifier_strategy_t strategy) { + switch (strategy) { + case universal_notifier_t::strategy_default: { assert(0 && "strategy_default should be passed"); break; - - case universal_notifier_t::strategy_shmem_polling: - // nothing required - break; - - case universal_notifier_t::strategy_notifyd: - // notifyd requires a round trip to the notifyd server, which means we have to wait a little bit to receive it - // In practice, this seems to be enough + } + case universal_notifier_t::strategy_shmem_polling: { + break; // nothing required + } + case universal_notifier_t::strategy_notifyd: { + // notifyd requires a round trip to the notifyd server, which means we have to wait a + // little bit to receive it. In practice, this seems to be enough. usleep(1000000 / 25); break; - + } case universal_notifier_t::strategy_named_pipe: - case universal_notifier_t::strategy_null: + case universal_notifier_t::strategy_null: { break; + } } } -static void test_notifiers_with_strategy(universal_notifier_t::notifier_strategy_t strategy) -{ +static void test_notifiers_with_strategy(universal_notifier_t::notifier_strategy_t strategy) { assert(strategy != universal_notifier_t::strategy_default); say(L"Testing universal notifiers with strategy %d", (int)strategy); universal_notifier_t *notifiers[16]; size_t notifier_count = sizeof notifiers / sizeof *notifiers; - // Populate array of notifiers - for (size_t i=0; i < notifier_count; i++) - { + // Populate array of notifiers. + for (size_t i = 0; i < notifier_count; i++) { notifiers[i] = universal_notifier_t::new_notifier_for_strategy(strategy, UVARS_TEST_PATH); } - // Nobody should poll yet - for (size_t i=0; i < notifier_count; i++) - { - if (poll_notifier(notifiers[i])) - { - err(L"Universal variable notifier polled true before any changes, with strategy %d", (int)strategy); + // Nobody should poll yet. + for (size_t i = 0; i < notifier_count; i++) { + if (poll_notifier(notifiers[i])) { + err(L"Universal variable notifier polled true before any changes, with strategy %d", + (int)strategy); } } // Tweak each notifier. Verify that others see it. - for (size_t post_idx=0; post_idx < notifier_count; post_idx++) - { + for (size_t post_idx = 0; post_idx < notifier_count; post_idx++) { notifiers[post_idx]->post_notification(); - // Do special stuff to "trigger" a notification for testing + // Do special stuff to "trigger" a notification for testing. trigger_or_wait_for_notification(notifiers[post_idx], strategy); - for (size_t i=0; i < notifier_count; i++) - { - // We aren't concerned with the one who posted - // Poll from it (to drain it), and then skip it - if (i == post_idx) - { + for (size_t i = 0; i < notifier_count; i++) { + // We aren't concerned with the one who posted. Poll from it (to drain it), and then + // skip it. + if (i == post_idx) { poll_notifier(notifiers[i]); continue; } - if (! poll_notifier(notifiers[i])) - { - err(L"Universal variable notifier (%lu) %p polled failed to notice changes, with strategy %d", i, notifiers[i], (int)strategy); + if (!poll_notifier(notifiers[i])) { + err(L"Universal variable notifier (%lu) %p polled failed to notice changes, with " + L"strategy %d", + i, notifiers[i], (int)strategy); } } - // Named pipes have special cleanup requirements - if (strategy == universal_notifier_t::strategy_named_pipe) - { - usleep(1000000 / 10); //corresponds to NAMED_PIPE_FLASH_DURATION_USEC - // Have to clean up the posted one first, so that the others see the pipe become no longer readable + // Named pipes have special cleanup requirements. + if (strategy == universal_notifier_t::strategy_named_pipe) { + usleep(1000000 / 10); // corresponds to NAMED_PIPE_FLASH_DURATION_USEC + // Have to clean up the posted one first, so that the others see the pipe become no + // longer readable. poll_notifier(notifiers[post_idx]); - for (size_t i=0; i < notifier_count; i++) - { + for (size_t i = 0; i < notifier_count; i++) { poll_notifier(notifiers[i]); } } } - // Nobody should poll now - for (size_t i=0; i < notifier_count; i++) - { - if (poll_notifier(notifiers[i])) - { - err(L"Universal variable notifier polled true after all changes, with strategy %d", (int)strategy); + // Nobody should poll now. + for (size_t i = 0; i < notifier_count; i++) { + if (poll_notifier(notifiers[i])) { + err(L"Universal variable notifier polled true after all changes, with strategy %d", + (int)strategy); } } - // Clean up - for (size_t i=0; i < notifier_count; i++) - { + // Clean up. + for (size_t i = 0; i < notifier_count; i++) { delete notifiers[i]; } } -static void test_universal_notifiers() -{ - if (system("mkdir -p /tmp/fish_uvars_test/ && touch /tmp/fish_uvars_test/varsfile.txt")) err(L"mkdir failed"); +static void test_universal_notifiers() { + if (system("mkdir -p /tmp/fish_uvars_test/ && touch /tmp/fish_uvars_test/varsfile.txt")) + err(L"mkdir failed"); test_notifiers_with_strategy(universal_notifier_t::strategy_shmem_polling); test_notifiers_with_strategy(universal_notifier_t::strategy_named_pipe); #if __APPLE__ @@ -2883,9 +2561,8 @@ static void test_universal_notifiers() if (system("rm -Rf /tmp/fish_uvars_test/")) err(L"rm failed"); } -class history_tests_t -{ -public: +class history_tests_t { + public: static void test_history(void); static void test_history_merge(void); static void test_history_formats(void); @@ -2894,20 +2571,17 @@ public: static void test_history_races_pound_on_history(); }; -static wcstring random_string(void) -{ +static wcstring random_string(void) { wcstring result; size_t max = 1 + rand() % 32; - while (max--) - { - wchar_t c = 1 + rand()%ESCAPE_TEST_CHAR; + while (max--) { + wchar_t c = 1 + rand() % ESCAPE_TEST_CHAR; result.push_back(c); } return result; } -void history_tests_t::test_history(void) -{ +void history_tests_t::test_history(void) { say(L"Testing history"); history_t &history = history_t::history_with_name(L"test_history"); @@ -2916,44 +2590,40 @@ void history_tests_t::test_history(void) history.add(L"Beta"); history.add(L"Alpha"); - /* All three items match "a" */ + // All three items match "a". history_search_t search1(history, L"a"); test_history_matches(search1, 3); do_test(search1.current_string() == L"Alpha"); - /* One item matches "et" */ + // One item matches "et". history_search_t search2(history, L"et"); test_history_matches(search2, 1); do_test(search2.current_string() == L"Beta"); - /* Test item removal */ + // Test item removal. history.remove(L"Alpha"); history_search_t search3(history, L"Alpha"); test_history_matches(search3, 0); - /* Test history escaping and unescaping, yaml, etc. */ + // Test history escaping and unescaping, yaml, etc. history_item_list_t before, after; history.clear(); size_t i, max = 100; - for (i=1; i <= max; i++) - { - - /* Generate a value */ + for (i = 1; i <= max; i++) { + // Generate a value. wcstring value = wcstring(L"test item ") + to_string(i); - /* Maybe add some backslashes */ - if (i % 3 == 0) - value.append(L"(slashies \\\\\\ slashies)"); + // Maybe add some backslashes. + if (i % 3 == 0) value.append(L"(slashies \\\\\\ slashies)"); - /* Generate some paths */ + // Generate some paths. path_list_t paths; size_t count = rand() % 6; - while (count--) - { + while (count--) { paths.push_back(random_string()); } - /* Record this item */ + // Record this item. history_item_t item(value, time(NULL)); item.required_paths = paths; before.push_back(item); @@ -2961,57 +2631,48 @@ 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--) - { + // Read items back in reverse order and ensure they're the same. + for (i = 100; i >= 1; i--) { history_item_t item = history.item_at_index(i); - do_test(! item.empty()); + do_test(!item.empty()); after.push_back(item); } do_test(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); do_test(bef.contents == aft.contents); do_test(bef.creation_timestamp == aft.creation_timestamp); do_test(bef.required_paths == aft.required_paths); } - /* Clean up after our tests */ + // Clean up after our tests. history.clear(); } -// wait until the next second -static void time_barrier(void) -{ +// Wait until the next second. +static void time_barrier(void) { time_t start = time(NULL); - do - { + do { usleep(1000); - } - while (time(NULL) == start); + } while (time(NULL) == start); } -static wcstring_list_t generate_history_lines(int pid) -{ +static wcstring_list_t generate_history_lines(int pid) { wcstring_list_t result; long max = 256; result.reserve(max); - for (long i=0; i < max; i++) - { + for (long i = 0; i < max; i++) { result.push_back(format_string(L"%ld %ld", (long)pid, i)); } return result; } -void history_tests_t::test_history_races_pound_on_history() -{ - /* Called in child process to modify history */ +void history_tests_t::test_history_races_pound_on_history() { + // Called in child process to modify history. history_t *hist = new history_t(L"race_test"); hist->chaos_mode = true; const wcstring_list_t lines = generate_history_lines(getpid()); - for (size_t idx = 0; idx < lines.size(); idx++) - { + for (size_t idx = 0; idx < lines.size(); idx++) { const wcstring &line = lines.at(idx); hist->add(line); hist->save(); @@ -3019,103 +2680,90 @@ void history_tests_t::test_history_races_pound_on_history() delete hist; } -void history_tests_t::test_history_races(void) -{ +void history_tests_t::test_history_races(void) { say(L"Testing history race conditions"); - // Ensure history is clear + // Ensure history is clear. history_t *hist = new history_t(L"race_test"); hist->clear(); delete hist; - // Test concurrent history writing +// Test concurrent history writing. #define RACE_COUNT 10 pid_t children[RACE_COUNT]; - for (size_t i=0; i < RACE_COUNT; i++) - { + for (size_t i = 0; i < RACE_COUNT; i++) { pid_t pid = fork(); - if (! pid) - { - // Child process + if (!pid) { + // Child process. setup_fork_guards(); test_history_races_pound_on_history(); exit_without_destructors(0); - } - else - { - // Parent process + } else { + // Parent process. children[i] = pid; } } - // Wait for all children - for (size_t i=0; i < RACE_COUNT; i++) - { + // Wait for all children. + for (size_t i = 0; i < RACE_COUNT; i++) { int stat; waitpid(children[i], &stat, WUNTRACED); } - // Compute the expected lines + // Compute the expected lines. wcstring_list_t lines[RACE_COUNT]; - for (size_t i=0; i < RACE_COUNT; i++) - { + for (size_t i = 0; i < RACE_COUNT; i++) { lines[i] = generate_history_lines(children[i]); } - // Count total lines + // Count total lines. size_t line_count = 0; - for (size_t i=0; i < RACE_COUNT; i++) - { + for (size_t i = 0; i < RACE_COUNT; i++) { line_count += lines[i].size(); } - // Ensure we consider the lines that have been outputted as part of our history + // Ensure we consider the lines that have been outputted as part of our history. time_barrier(); - /* Ensure that we got sane, sorted results */ + // Ensure that we got sane, sorted results. hist = new history_t(L"race_test"); hist->chaos_mode = true; size_t hist_idx; - for (hist_idx = 1; ; hist_idx ++) - { + for (hist_idx = 1;; hist_idx++) { history_item_t item = hist->item_at_index(hist_idx); - if (item.empty()) - break; + if (item.empty()) break; - // The item must be present in one of our 'lines' arrays - // If it is present, then every item after it is assumed to be missed + // The item must be present in one of our 'lines' arrays. If it is present, then every item + // after it is assumed to be missed. size_t i; - for (i=0; i < RACE_COUNT; i++) - { - wcstring_list_t::iterator where = std::find(lines[i].begin(), lines[i].end(), item.str()); - if (where != lines[i].end()) - { - // Delete everything from the found location onwards + for (i = 0; i < RACE_COUNT; i++) { + wcstring_list_t::iterator where = + std::find(lines[i].begin(), lines[i].end(), item.str()); + if (where != lines[i].end()) { + // Delete everything from the found location onwards. lines[i].resize(where - lines[i].begin()); - // Break because we found it + // Break because we found it. break; } } - if (i >= RACE_COUNT) - { + if (i >= RACE_COUNT) { err(L"Line '%ls' found in history not found in some array", item.str().c_str()); } } - // every write should add at least one item + // Every write should add at least one item. do_test(hist_idx >= RACE_COUNT); - //hist->clear(); + // hist->clear(); delete hist; } -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. +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"); const size_t count = 3; const wcstring name = L"merge_test"; @@ -3123,128 +2771,108 @@ void history_tests_t::test_history_merge(void) const wcstring texts[count] = {L"History 1", L"History 2", L"History 3"}; const wcstring alt_texts[count] = {L"History Alt 1", L"History Alt 2", L"History Alt 3"}; - /* Make sure history is clear */ - for (size_t i=0; i < count; i++) - { + // Make sure history is clear. + for (size_t i = 0; i < count; i++) { hists[i]->clear(); } - /* Make sure we don't add an item in the same second as we created the history */ + // Make sure we don't add an item in the same second as we created the history. time_barrier(); - /* Add a different item to each */ - for (size_t i=0; i < count; i++) - { + // Add a different item to each. + for (size_t i = 0; i < count; i++) { hists[i]->add(texts[i]); } - /* Save them */ - for (size_t i=0; i < count; i++) - { + // Save them. + for (size_t i = 0; i < count; i++) { hists[i]->save(); } - /* Make sure each history contains what it ought to, but they have not leaked into each other */ - for (size_t i = 0; i < count; i++) - { - for (size_t j=0; j < count; j++) - { + // 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++) { bool does_contain = history_contains(hists[i], texts[j]); bool should_contain = (i == j); do_test(should_contain == does_contain); } } - /* Make a new history. It should contain everything. The time_barrier() is so that the timestamp is newer, since we only pick up items whose timestamp is before the birth stamp. */ + // 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++) { do_test(history_contains(everything, texts[i])); } - /* Tell all histories to merge. Now everybody should have everything. */ - for (size_t i=0; i < count; i++) - { + // Tell all histories to merge. Now everybody should have everything. + for (size_t i = 0; i < count; i++) { hists[i]->incorporate_external_changes(); } - /* Add some more per-history items */ - for (size_t i=0; i < count; i++) - { + // Add some more per-history items. + for (size_t i = 0; i < count; i++) { hists[i]->add(alt_texts[i]); } - /* Everybody should have old items, but only one history should have each new item */ - for (size_t i = 0; i < count; i++) - { - for (size_t j=0; j < count; j++) - { - /* Old item */ + // Everybody should have old items, but only one history should have each new item. + for (size_t i = 0; i < count; i++) { + for (size_t j = 0; j < count; j++) { + // Old item. do_test(history_contains(hists[i], texts[j])); - /* New item */ + // New item. bool does_contain = history_contains(hists[i], alt_texts[j]); bool should_contain = (i == j); do_test(should_contain == does_contain); } } - - /* Clean up */ - for (size_t i=0; i < 3; i++) - { + // Clean up. + for (size_t i = 0; i < 3; i++) { delete hists[i]; } everything->clear(); - delete everything; //not as scary as it looks + 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) { wcstring path; - if (! path_get_data(path)) { + if (!path_get_data(path)) { err(L"Failed to get data directory"); return false; } char command[512]; snprintf(command, sizeof command, "cp tests/%ls %ls/%ls_history", name, path.c_str(), name); - if (system(command)) - { + if (system(command)) { err(L"Failed to copy sample history"); return false; } return true; } -/* 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) -{ - /* Count our expected items */ +/// 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) { + // 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 */ + // 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) - { - err(L"Expected '%ls', found '%ls' at index %lu", expected, item.str().c_str(), history_idx); + } else { + if (item.str() != expected) { + err(L"Expected '%ls', found '%ls' at index %lu", expected, item.str().c_str(), + history_idx); } } history_idx++; @@ -3254,40 +2882,21 @@ 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 + // 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 - { - /* Note: This is backwards from what appears in the file */ - const wchar_t * const expected[] = - { - L"#def", - - L"echo #abc", - - L"function yay\n" - "echo hi\n" - "end", - - L"cd foobar", - - L"ls /", - - NULL - }; + } else { + // Note: This is backwards from what appears in the file. + const wchar_t *const expected[] = { + L"#def", L"echo #abc", L"function yay\necho hi\nend", L"cd foobar", L"ls /", NULL}; 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(); @@ -3295,28 +2904,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[] = - { - L"echo this has\\\nbackslashes", - - L"function foo\n" - "echo bar\n" - "end", - - L"echo alpha", - - NULL - }; + } else { + const wchar_t *const expected[] = {L"echo this has\\\nbackslashes", + L"function foo\necho bar\nend", L"echo alpha", NULL}; 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(); @@ -3324,27 +2919,14 @@ 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 - { - // It should skip over the export command since that's a bash-ism - const wchar_t *expected[] = - { - L"echo supsup", - - L"history --help", - - L"echo foo", - - NULL - }; + } else { + // It should skip over the export command since that's a bash-ism. + const wchar_t *expected[] = {L"echo supsup", L"history --help", L"echo foo", NULL}; history_t &test_history = history_t::history_with_name(L"bash_import"); 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(); @@ -3353,26 +2935,14 @@ void history_tests_t::test_history_formats(void) name = L"history_sample_corrupt1"; 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 - { - /* We simply invoke get_string_representation. If we don't die, the test is a success. */ + } else { + // We simply invoke get_string_representation. If we don't die, the test is a success. history_t &test_history = history_t::history_with_name(name); - const wchar_t *expected[] = - { - L"no_newline_at_end_of_file", - - L"corrupt_prefix", - - L"this_command_is_ok", - - NULL - }; - if (! history_equals(test_history, expected)) - { + const wchar_t *expected[] = {L"no_newline_at_end_of_file", L"corrupt_prefix", + L"this_command_is_ok", NULL}; + if (!history_equals(test_history, expected)) { err(L"test_history_formats failed for %ls\n", name); } test_history.clear(); @@ -3387,7 +2957,7 @@ void history_tests_t::test_history_speed(void) history_t *hist = new history_t(L"speed_test"); wcstring item = L"History Speed Test - X"; - /* Test for 10 seconds */ + // Test for 10 seconds. double start = timef(); double end = start + 10; double stop = 0; @@ -3408,16 +2978,12 @@ void history_tests_t::test_history_speed(void) } #endif -static void test_new_parser_correctness(void) -{ +static void test_new_parser_correctness(void) { say(L"Testing new parser!"); - const struct parser_test_t - { + const struct parser_test_t { const wchar_t *src; bool ok; - } - parser_tests[] = - { + } parser_tests[] = { {L"; ; ; ", true}, {L"if ; end", false}, {L"if true ; end", true}, @@ -3433,71 +2999,49 @@ static void test_new_parser_correctness(void) {L"begin if true ; echo hi ; end; end", true}, }; - for (size_t i=0; i < sizeof parser_tests / sizeof *parser_tests; i++) - { + for (size_t i = 0; i < sizeof parser_tests / sizeof *parser_tests; i++) { const parser_test_t *test = &parser_tests[i]; parse_node_tree_t parse_tree; bool success = parse_tree_from_string(test->src, parse_flag_none, &parse_tree, NULL); - say(L"%lu / %lu: Parse \"%ls\": %s", i+1, sizeof parser_tests / sizeof *parser_tests, test->src, success ? "yes" : "no"); - if (success && ! test->ok) - { + say(L"%lu / %lu: Parse \"%ls\": %s", i + 1, sizeof parser_tests / sizeof *parser_tests, + test->src, success ? "yes" : "no"); + if (success && !test->ok) { err(L"\"%ls\" should NOT have parsed, but did", test->src); - } - else if (! success && test->ok) - { + } else if (!success && test->ok) { err(L"\"%ls\" should have parsed, but failed", test->src); } } say(L"Parse tests complete"); } -/* Given that we have an array of 'fuzz_count' strings, we wish to enumerate all permutations of 'len' values. We do this by incrementing an integer, interpreting it as "base fuzz_count". */ -static inline bool string_for_permutation(const wcstring *fuzzes, size_t fuzz_count, size_t len, size_t permutation, wcstring *out_str) -{ +// Given that we have an array of 'fuzz_count' strings, we wish to enumerate all permutations of +// 'len' values. We do this by incrementing an integer, interpreting it as "base fuzz_count". +static inline bool string_for_permutation(const wcstring *fuzzes, size_t fuzz_count, size_t len, + size_t permutation, wcstring *out_str) { out_str->clear(); size_t remaining_permutation = permutation; - for (size_t i=0; i < len; i++) - { + for (size_t i = 0; i < len; i++) { size_t idx = remaining_permutation % fuzz_count; remaining_permutation /= fuzz_count; out_str->append(fuzzes[idx]); out_str->push_back(L' '); } - // Return false if we wrapped + // Return false if we wrapped. return remaining_permutation == 0; } -static void test_new_parser_fuzzing(void) -{ +static void test_new_parser_fuzzing(void) { say(L"Fuzzing parser (node size: %lu)", sizeof(parse_node_t)); - const wcstring fuzzes[] = - { - L"if", - L"else", - L"for", - L"in", - L"while", - L"begin", - L"function", - L"switch", - L"case", - L"end", - L"and", - L"or", - L"not", - L"command", - L"builtin", - L"foo", - L"|", - L"^", - L"&", - L";", + const wcstring fuzzes[] = { + L"if", L"else", L"for", L"in", L"while", L"begin", L"function", + L"switch", L"case", L"end", L"and", L"or", L"not", L"command", + L"builtin", L"foo", L"|", L"^", L"&", L";", }; - /* Generate a list of strings of all keyword / token combinations. */ + // Generate a list of strings of all keyword / token combinations. wcstring src; src.reserve(128); @@ -3507,56 +3051,53 @@ static void test_new_parser_fuzzing(void) double start = timef(); bool log_it = true; unsigned long max_len = 5; - for (unsigned long len = 0; len < max_len; len++) - { - if (log_it) - fprintf(stderr, "%lu / %lu...", len, max_len); + for (unsigned long len = 0; len < max_len; len++) { + if (log_it) fprintf(stderr, "%lu / %lu...", len, max_len); - /* We wish to look at all permutations of 4 elements of 'fuzzes' (with replacement). Construct an int and keep incrementing it. */ + // We wish to look at all permutations of 4 elements of 'fuzzes' (with replacement). + // Construct an int and keep incrementing it. unsigned long permutation = 0; - while (string_for_permutation(fuzzes, sizeof fuzzes / sizeof *fuzzes, len, permutation++, &src)) - { + while (string_for_permutation(fuzzes, sizeof fuzzes / sizeof *fuzzes, len, permutation++, + &src)) { parse_tree_from_string(src, parse_flag_continue_after_error, &node_tree, &errors); } - if (log_it) - fprintf(stderr, "done (%lu)\n", permutation); - + if (log_it) fprintf(stderr, "done (%lu)\n", permutation); } double end = timef(); - if (log_it) - say(L"All fuzzed in %f seconds!", end - start); + if (log_it) say(L"All fuzzed in %f seconds!", end - start); } -// Parse a statement, returning the command, args (joined by spaces), and the decoration. Returns true if successful. -static bool test_1_parse_ll2(const wcstring &src, wcstring *out_cmd, wcstring *out_joined_args, enum parse_statement_decoration_t *out_deco) -{ +// Parse a statement, returning the command, args (joined by spaces), and the decoration. Returns +// true if successful. +static bool test_1_parse_ll2(const wcstring &src, wcstring *out_cmd, wcstring *out_joined_args, + enum parse_statement_decoration_t *out_deco) { out_cmd->clear(); out_joined_args->clear(); *out_deco = parse_statement_decoration_none; bool result = false; parse_node_tree_t tree; - if (parse_tree_from_string(src, parse_flag_none, &tree, NULL)) - { - /* Get the statement. Should only have one */ - const parse_node_tree_t::parse_node_list_t stmt_nodes = tree.find_nodes(tree.at(0), symbol_plain_statement); - if (stmt_nodes.size() != 1) - { - say(L"Unexpected number of statements (%lu) found in '%ls'", stmt_nodes.size(), src.c_str()); + if (parse_tree_from_string(src, parse_flag_none, &tree, NULL)) { + // Get the statement. Should only have one. + const parse_node_tree_t::parse_node_list_t stmt_nodes = + tree.find_nodes(tree.at(0), symbol_plain_statement); + if (stmt_nodes.size() != 1) { + say(L"Unexpected number of statements (%lu) found in '%ls'", stmt_nodes.size(), + src.c_str()); return false; } const parse_node_t &stmt = *stmt_nodes.at(0); - /* Return its decoration */ + // Return its decoration. *out_deco = tree.decoration_for_plain_statement(stmt); - /* Return its command */ + // Return its command. tree.command_for_plain_statement(stmt, src, out_cmd); - /* Return arguments separated by spaces */ - const parse_node_tree_t::parse_node_list_t arg_nodes = tree.find_nodes(stmt, symbol_argument); - for (size_t i=0; i < arg_nodes.size(); i++) - { + // Return arguments separated by spaces. + const parse_node_tree_t::parse_node_list_t arg_nodes = + tree.find_nodes(stmt, symbol_argument); + for (size_t i = 0; i < arg_nodes.size(); i++) { if (i > 0) out_joined_args->push_back(L' '); out_joined_args->append(arg_nodes.at(i)->get_source(src)); } @@ -3565,19 +3106,19 @@ static bool test_1_parse_ll2(const wcstring &src, wcstring *out_cmd, wcstring *o return result; } -/* Test the LL2 (two token lookahead) nature of the parser by exercising the special builtin and command handling. In particular, 'command foo' should be a decorated statement 'foo' but 'command --help' should be an undecorated statement 'command' with argument '--help', and NOT attempt to run a command called '--help' */ -static void test_new_parser_ll2(void) -{ +// Test the LL2 (two token lookahead) nature of the parser by exercising the special builtin and +// command handling. In particular, 'command foo' should be a decorated statement 'foo' but 'command +// -help' should be an undecorated statement 'command' with argument '--help', and NOT attempt to +// run a command called '--help'. +static void test_new_parser_ll2(void) { say(L"Testing parser two-token lookahead"); - const struct - { + const struct { wcstring src; wcstring cmd; wcstring args; enum parse_statement_decoration_t deco; - } tests[] = - { + } tests[] = { {L"echo hello", L"echo", L"hello", parse_statement_decoration_none}, {L"command echo hello", L"echo", L"hello", parse_statement_decoration_command}, {L"exec echo hello", L"echo", L"hello", parse_statement_decoration_exec}, @@ -3590,90 +3131,80 @@ static void test_new_parser_ll2(void) {L"command --", L"command", L"--", parse_statement_decoration_none}, {L"builtin --names", L"builtin", L"--names", parse_statement_decoration_none}, {L"function", L"function", L"", parse_statement_decoration_none}, - {L"function --help", L"function", L"--help", parse_statement_decoration_none} - }; + {L"function --help", L"function", L"--help", parse_statement_decoration_none}}; - for (size_t i=0; i < sizeof tests / sizeof *tests; i++) - { + for (size_t i = 0; i < sizeof tests / sizeof *tests; i++) { wcstring cmd, args; enum parse_statement_decoration_t deco = parse_statement_decoration_none; bool success = test_1_parse_ll2(tests[i].src, &cmd, &args, &deco); - if (! success) + if (!success) err(L"Parse of '%ls' failed on line %ld", tests[i].cmd.c_str(), (long)__LINE__); if (cmd != tests[i].cmd) - err(L"When parsing '%ls', expected command '%ls' but got '%ls' on line %ld", tests[i].src.c_str(), tests[i].cmd.c_str(), cmd.c_str(), (long)__LINE__); + err(L"When parsing '%ls', expected command '%ls' but got '%ls' on line %ld", + tests[i].src.c_str(), tests[i].cmd.c_str(), cmd.c_str(), (long)__LINE__); if (args != tests[i].args) - err(L"When parsing '%ls', expected args '%ls' but got '%ls' on line %ld", tests[i].src.c_str(), tests[i].args.c_str(), args.c_str(), (long)__LINE__); + err(L"When parsing '%ls', expected args '%ls' but got '%ls' on line %ld", + tests[i].src.c_str(), tests[i].args.c_str(), args.c_str(), (long)__LINE__); if (deco != tests[i].deco) - err(L"When parsing '%ls', expected decoration %d but got %d on line %ld", tests[i].src.c_str(), (int)tests[i].deco, (int)deco, (long)__LINE__); + err(L"When parsing '%ls', expected decoration %d but got %d on line %ld", + tests[i].src.c_str(), (int)tests[i].deco, (int)deco, (long)__LINE__); } - /* Verify that 'function -h' and 'function --help' are plain statements but 'function --foo' is not (#1240) */ - const struct - { + // Verify that 'function -h' and 'function --help' are plain statements but 'function --foo' is + // not (issue #1240). + const struct { wcstring src; parse_token_type_t type; - } - tests2[] = - { + } tests2[] = { {L"function -h", symbol_plain_statement}, {L"function --help", symbol_plain_statement}, {L"function --foo ; end", symbol_function_header}, {L"function foo ; end", symbol_function_header}, }; - for (size_t i=0; i < sizeof tests2 / sizeof *tests2; i++) - { + for (size_t i = 0; i < sizeof tests2 / sizeof *tests2; i++) { parse_node_tree_t tree; - if (! parse_tree_from_string(tests2[i].src, parse_flag_none, &tree, NULL)) - { + if (!parse_tree_from_string(tests2[i].src, parse_flag_none, &tree, NULL)) { err(L"Failed to parse '%ls'", tests2[i].src.c_str()); } - const parse_node_tree_t::parse_node_list_t node_list = tree.find_nodes(tree.at(0), tests2[i].type); - if (node_list.size() == 0) - { + const parse_node_tree_t::parse_node_list_t node_list = + tree.find_nodes(tree.at(0), tests2[i].type); + if (node_list.size() == 0) { err(L"Failed to find node of type '%ls'", token_type_description(tests2[i].type)); - } - else if (node_list.size() > 1) - { + } else if (node_list.size() > 1) { err(L"Found too many nodes of type '%ls'", token_type_description(tests2[i].type)); } } } -static void test_new_parser_ad_hoc() -{ - /* Very ad-hoc tests for issues encountered */ +static void test_new_parser_ad_hoc() { + // Very ad-hoc tests for issues encountered. say(L"Testing new parser ad hoc tests"); - /* Ensure that 'case' terminates a job list */ + // Ensure that 'case' terminates a job list. const wcstring src = L"switch foo ; case bar; case baz; end"; parse_node_tree_t parse_tree; bool success = parse_tree_from_string(src, parse_flag_none, &parse_tree, NULL); - if (! success) - { + if (!success) { err(L"Parsing failed"); } - /* Expect three case_item_lists: one for each case, and a terminal one. The bug was that we'd try to run a command 'case' */ + // Expect three case_item_lists: one for each case, and a terminal one. The bug was that we'd + // try to run a command 'case'. const parse_node_t &root = parse_tree.at(0); - const parse_node_tree_t::parse_node_list_t node_list = parse_tree.find_nodes(root, symbol_case_item_list); - if (node_list.size() != 3) - { + const parse_node_tree_t::parse_node_list_t node_list = + parse_tree.find_nodes(root, symbol_case_item_list); + if (node_list.size() != 3) { err(L"Expected 3 case item nodes, found %lu", node_list.size()); } } -static void test_new_parser_errors(void) -{ +static void test_new_parser_errors(void) { say(L"Testing new parser error reporting"); - const struct - { + const struct { const wchar_t *src; parse_error_code_t code; - } - tests[] = - { + } tests[] = { {L"echo 'abc", parse_error_tokenizer_unterminated_quote}, {L"'", parse_error_tokenizer_unterminated_quote}, {L"echo (abc", parse_error_tokenizer_unterminated_subshell}, @@ -3691,86 +3222,70 @@ static void test_new_parser_errors(void) {L"foo && bar", parse_error_double_background}, }; - for (size_t i = 0; i < sizeof tests / sizeof *tests; i++) - { + for (size_t i = 0; i < sizeof tests / sizeof *tests; i++) { const wcstring src = tests[i].src; parse_error_code_t expected_code = tests[i].code; parse_error_list_t errors; parse_node_tree_t parse_tree; bool success = parse_tree_from_string(src, parse_flag_none, &parse_tree, &errors); - if (success) - { + if (success) { err(L"Source '%ls' was expected to fail to parse, but succeeded", src.c_str()); } - if (errors.size() != 1) - { - err(L"Source '%ls' was expected to produce 1 error, but instead produced %lu errors", src.c_str(), errors.size()); - } - else if (errors.at(0).code != expected_code) - { - err(L"Source '%ls' was expected to produce error code %lu, but instead produced error code %lu", src.c_str(), expected_code, (unsigned long)errors.at(0).code); - for (size_t i=0; i < errors.size(); i++) - { + if (errors.size() != 1) { + err(L"Source '%ls' was expected to produce 1 error, but instead produced %lu errors", + src.c_str(), errors.size()); + } else if (errors.at(0).code != expected_code) { + err(L"Source '%ls' was expected to produce error code %lu, but instead produced error " + L"code %lu", + src.c_str(), expected_code, (unsigned long)errors.at(0).code); + for (size_t i = 0; i < errors.size(); i++) { err(L"\t\t%ls", errors.at(i).describe(src).c_str()); } } - } } -/* Given a format string, returns a list of non-empty strings separated by format specifiers. The format specifiers themselves are omitted. */ -static wcstring_list_t separate_by_format_specifiers(const wchar_t *format) -{ +// Given a format string, returns a list of non-empty strings separated by format specifiers. The +// format specifiers themselves are omitted. +static wcstring_list_t separate_by_format_specifiers(const wchar_t *format) { wcstring_list_t result; const wchar_t *cursor = format; const wchar_t *end = format + wcslen(format); - while (cursor < end) - { + while (cursor < end) { const wchar_t *next_specifier = wcschr(cursor, '%'); - if (next_specifier == NULL) - { + if (next_specifier == NULL) { next_specifier = end; } assert(next_specifier != NULL); - /* Don't return empty strings */ - if (next_specifier > cursor) - { + // Don't return empty strings. + if (next_specifier > cursor) { result.push_back(wcstring(cursor, next_specifier - cursor)); } - /* Walk over the format specifier (if any) */ + // Walk over the format specifier (if any). cursor = next_specifier; - if (*cursor == '%') - { + if (*cursor == '%') { cursor++; // Flag - if (wcschr(L"#0- +'", *cursor)) - cursor++; + if (wcschr(L"#0- +'", *cursor)) cursor++; // Minimum field width - while (iswdigit(*cursor)) - cursor++; + while (iswdigit(*cursor)) cursor++; // Precision - if (*cursor == L'.') - { + if (*cursor == L'.') { cursor++; - while (iswdigit(*cursor)) - cursor++; + while (iswdigit(*cursor)) cursor++; } // Length modifier - if (! wcsncmp(cursor, L"ll", 2) || ! wcsncmp(cursor, L"hh", 2)) - { + if (!wcsncmp(cursor, L"ll", 2) || !wcsncmp(cursor, L"hh", 2)) { cursor += 2; - } - else if (wcschr(L"hljtzqL", *cursor)) - { + } else if (wcschr(L"hljtzqL", *cursor)) { cursor++; } - // The format specifier itself. We allow any character except NUL - if (*cursor != L'\0') - { + // The format specifier itself. We allow any character except NUL. + if (*cursor != L'\0') { cursor += 1; } assert(cursor <= end); @@ -3779,18 +3294,17 @@ static wcstring_list_t separate_by_format_specifiers(const wchar_t *format) return result; } -/* Given a format string 'format', return true if the string may have been produced by that format string. We do this by splitting the format string around the format specifiers, and then ensuring that each of the remaining chunks is found (in order) in the string. */ -static bool string_matches_format(const wcstring &string, const wchar_t *format) -{ +// Given a format string 'format', return true if the string may have been produced by that format +// string. We do this by splitting the format string around the format specifiers, and then ensuring +// that each of the remaining chunks is found (in order) in the string. +static bool string_matches_format(const wcstring &string, const wchar_t *format) { bool result = true; wcstring_list_t components = separate_by_format_specifiers(format); size_t idx = 0; - for (size_t i=0; i < components.size(); i++) - { + for (size_t i = 0; i < components.size(); i++) { const wcstring &component = components.at(i); size_t where = string.find(component, idx); - if (where == wcstring::npos) - { + if (where == wcstring::npos) { result = false; break; } @@ -3800,83 +3314,68 @@ static bool string_matches_format(const wcstring &string, const wchar_t *format) return result; } -static void test_error_messages() -{ +static void test_error_messages() { say(L"Testing error messages"); - const struct error_test_t - { + const struct error_test_t { const wchar_t *src; const wchar_t *error_text_format; - } - error_tests[] = - { - {L"echo $^", ERROR_BAD_VAR_CHAR1}, - {L"echo foo${a}bar", ERROR_BRACKETED_VARIABLE1}, - {L"echo foo\"${a}\"bar", ERROR_BRACKETED_VARIABLE_QUOTED1}, - {L"echo foo\"${\"bar", ERROR_BAD_VAR_CHAR1}, - {L"echo $?", ERROR_NOT_STATUS}, - {L"echo $$", ERROR_NOT_PID}, - {L"echo $#", ERROR_NOT_ARGV_COUNT}, - {L"echo $@", ERROR_NOT_ARGV_AT}, - {L"echo $*", ERROR_NOT_ARGV_STAR}, - {L"echo $", ERROR_NO_VAR_NAME}, - {L"echo foo\"$\"bar", ERROR_NO_VAR_NAME}, - {L"echo \"foo\"$\"bar\"", ERROR_NO_VAR_NAME}, - {L"echo foo $ bar", ERROR_NO_VAR_NAME}, - {L"echo foo$(foo)bar", ERROR_BAD_VAR_SUBCOMMAND1}, - {L"echo \"foo$(foo)bar\"", ERROR_BAD_VAR_SUBCOMMAND1}, - {L"echo foo || echo bar", ERROR_BAD_OR}, - {L"echo foo && echo bar", ERROR_BAD_AND} - }; + } error_tests[] = {{L"echo $^", ERROR_BAD_VAR_CHAR1}, + {L"echo foo${a}bar", ERROR_BRACKETED_VARIABLE1}, + {L"echo foo\"${a}\"bar", ERROR_BRACKETED_VARIABLE_QUOTED1}, + {L"echo foo\"${\"bar", ERROR_BAD_VAR_CHAR1}, + {L"echo $?", ERROR_NOT_STATUS}, + {L"echo $$", ERROR_NOT_PID}, + {L"echo $#", ERROR_NOT_ARGV_COUNT}, + {L"echo $@", ERROR_NOT_ARGV_AT}, + {L"echo $*", ERROR_NOT_ARGV_STAR}, + {L"echo $", ERROR_NO_VAR_NAME}, + {L"echo foo\"$\"bar", ERROR_NO_VAR_NAME}, + {L"echo \"foo\"$\"bar\"", ERROR_NO_VAR_NAME}, + {L"echo foo $ bar", ERROR_NO_VAR_NAME}, + {L"echo foo$(foo)bar", ERROR_BAD_VAR_SUBCOMMAND1}, + {L"echo \"foo$(foo)bar\"", ERROR_BAD_VAR_SUBCOMMAND1}, + {L"echo foo || echo bar", ERROR_BAD_OR}, + {L"echo foo && echo bar", ERROR_BAD_AND}}; parse_error_list_t errors; - for (size_t i=0; i < sizeof error_tests / sizeof *error_tests; i++) - { + for (size_t i = 0; i < sizeof error_tests / sizeof *error_tests; i++) { const struct error_test_t *test = &error_tests[i]; errors.clear(); parse_util_detect_errors(test->src, &errors, false /* allow_incomplete */); - do_test(! errors.empty()); - if (! errors.empty()) - { + do_test(!errors.empty()); + if (!errors.empty()) { do_test1(string_matches_format(errors.at(0).text, test->error_text_format), test->src); } } } -static void test_highlighting(void) -{ +static void test_highlighting(void) { say(L"Testing syntax highlighting"); if (system("mkdir -p /tmp/fish_highlight_test/")) err(L"mkdir failed"); if (system("touch /tmp/fish_highlight_test/foo")) err(L"touch failed"); if (system("touch /tmp/fish_highlight_test/bar")) err(L"touch failed"); - // Here are the components of our source and the colors we expect those to be - struct highlight_component_t - { + // Here are the components of our source and the colors we expect those to be. + struct highlight_component_t { const wchar_t *txt; int color; }; - const highlight_component_t components1[] = - { + const highlight_component_t components1[] = { {L"echo", highlight_spec_command}, {L"/tmp/fish_highlight_test/foo", highlight_spec_param | highlight_modifier_valid_path}, {L"&", highlight_spec_statement_terminator}, - {NULL, -1} - }; + {NULL, -1}}; - const highlight_component_t components2[] = - { + const highlight_component_t components2[] = { {L"command", highlight_spec_command}, {L"echo", highlight_spec_command}, {L"abc", highlight_spec_param}, {L"/tmp/fish_highlight_test/foo", highlight_spec_param | highlight_modifier_valid_path}, {L"&", highlight_spec_statement_terminator}, - {NULL, -1} - }; + {NULL, -1}}; - const highlight_component_t components3[] = - { + const highlight_component_t components3[] = { {L"if command ls", highlight_spec_command}, {L"; ", highlight_spec_statement_terminator}, {L"echo", highlight_spec_command}, @@ -3885,167 +3384,138 @@ static void test_highlighting(void) {L"/bin/definitely_not_a_command", highlight_spec_error}, {L"; ", highlight_spec_statement_terminator}, {L"end", highlight_spec_command}, - {NULL, -1} - }; + {NULL, -1}}; - /* Verify that cd shows errors for non-directories */ - const highlight_component_t components4[] = - { + // Verify that cd shows errors for non-directories. + const highlight_component_t components4[] = { {L"cd", highlight_spec_command}, {L"/tmp/fish_highlight_test", highlight_spec_param | highlight_modifier_valid_path}, - {NULL, -1} - }; + {NULL, -1}}; - const highlight_component_t components5[] = - { + const highlight_component_t components5[] = { {L"cd", highlight_spec_command}, {L"/tmp/fish_highlight_test/foo", highlight_spec_error}, - {NULL, -1} - }; + {NULL, -1}}; - const highlight_component_t components6[] = - { + const highlight_component_t components6[] = { {L"cd", highlight_spec_command}, {L"--help", highlight_spec_param}, {L"-h", highlight_spec_param}, {L"definitely_not_a_directory", highlight_spec_error}, - {NULL, -1} - }; + {NULL, -1}}; - // Command substitutions - const highlight_component_t components7[] = - { - {L"echo", highlight_spec_command}, - {L"param1", highlight_spec_param}, - {L"(", highlight_spec_operator}, - {L"ls", highlight_spec_command}, - {L"param2", highlight_spec_param}, - {L")", highlight_spec_operator}, - {L"|", highlight_spec_statement_terminator}, - {L"cat", highlight_spec_command}, - {NULL, -1} - }; + // Command substitutions. + const highlight_component_t components7[] = {{L"echo", highlight_spec_command}, + {L"param1", highlight_spec_param}, + {L"(", highlight_spec_operator}, + {L"ls", highlight_spec_command}, + {L"param2", highlight_spec_param}, + {L")", highlight_spec_operator}, + {L"|", highlight_spec_statement_terminator}, + {L"cat", highlight_spec_command}, + {NULL, -1}}; - // Redirections substitutions - const highlight_component_t components8[] = - { + // Redirections substitutions. + const highlight_component_t components8[] = { {L"echo", highlight_spec_command}, {L"param1", highlight_spec_param}, - /* Input redirection */ + // Input redirection. {L"<", highlight_spec_redirection}, {L"/bin/echo", highlight_spec_redirection}, - /* Output redirection to a valid fd */ + // Output redirection to a valid fd. {L"1>&2", highlight_spec_redirection}, - /* Output redirection to an invalid fd */ + // Output redirection to an invalid fd. {L"2>&", highlight_spec_redirection}, {L"LOL", highlight_spec_error}, - /* Just a param, not a redirection */ + // Just a param, not a redirection. {L"/tmp/blah", highlight_spec_param}, - /* Input redirection from directory */ + // Input redirection from directory. {L"<", highlight_spec_redirection}, {L"/tmp/", highlight_spec_error}, - /* Output redirection to an invalid path */ + // Output redirection to an invalid path. {L"3>", highlight_spec_redirection}, {L"/not/a/valid/path/nope", highlight_spec_error}, - /* Output redirection to directory */ + // Output redirection to directory. {L"3>", highlight_spec_redirection}, {L"/tmp/nope/", highlight_spec_error}, - - /* Redirections to overflow fd */ + // Redirections to overflow fd. {L"99999999999999999999>&2", highlight_spec_error}, {L"2>&", highlight_spec_redirection}, {L"99999999999999999999", highlight_spec_error}, - /* Output redirection containing a command substitution */ + // Output redirection containing a command substitution. {L"4>", highlight_spec_redirection}, {L"(", highlight_spec_operator}, {L"echo", highlight_spec_command}, {L"/tmp/somewhere", highlight_spec_param}, {L")", highlight_spec_operator}, - /* Just another param */ + // Just another param. {L"param2", highlight_spec_param}, - {NULL, -1} - }; + {NULL, -1}}; - const highlight_component_t components9[] = - { - {L"end", highlight_spec_error}, - {L";", highlight_spec_statement_terminator}, - {L"if", highlight_spec_command}, - {L"end", highlight_spec_error}, - {NULL, -1} - }; + const highlight_component_t components9[] = {{L"end", highlight_spec_error}, + {L";", highlight_spec_statement_terminator}, + {L"if", highlight_spec_command}, + {L"end", highlight_spec_error}, + {NULL, -1}}; - const highlight_component_t components10[] = - { - {L"echo", highlight_spec_command}, - {L"'single_quote", highlight_spec_error}, - {NULL, -1} - }; + const highlight_component_t components10[] = { + {L"echo", highlight_spec_command}, {L"'single_quote", highlight_spec_error}, {NULL, -1}}; - const highlight_component_t components11[] = - { - {L"echo", highlight_spec_command}, - {L"$foo", highlight_spec_operator}, - {L"\"", highlight_spec_quote}, - {L"$bar", highlight_spec_operator}, - {L"\"", highlight_spec_quote}, - {L"$baz[", highlight_spec_operator}, - {L"1 2..3", highlight_spec_param}, - {L"]", highlight_spec_operator}, - {NULL, -1} - }; + const highlight_component_t components11[] = {{L"echo", highlight_spec_command}, + {L"$foo", highlight_spec_operator}, + {L"\"", highlight_spec_quote}, + {L"$bar", highlight_spec_operator}, + {L"\"", highlight_spec_quote}, + {L"$baz[", highlight_spec_operator}, + {L"1 2..3", highlight_spec_param}, + {L"]", highlight_spec_operator}, + {NULL, -1}}; - const highlight_component_t components12[] = - { - {L"for", highlight_spec_command}, - {L"i", highlight_spec_param}, - {L"in", highlight_spec_command}, - {L"1 2 3", highlight_spec_param}, - {L";", highlight_spec_statement_terminator}, - {L"end", highlight_spec_command}, - {NULL, -1} - }; + const highlight_component_t components12[] = {{L"for", highlight_spec_command}, + {L"i", highlight_spec_param}, + {L"in", highlight_spec_command}, + {L"1 2 3", highlight_spec_param}, + {L";", highlight_spec_statement_terminator}, + {L"end", highlight_spec_command}, + {NULL, -1}}; - const highlight_component_t components13[] = - { + const highlight_component_t components13[] = { {L"echo", highlight_spec_command}, {L"$$foo[", highlight_spec_operator}, {L"1", highlight_spec_param}, {L"][", highlight_spec_operator}, {L"2", highlight_spec_param}, {L"]", highlight_spec_operator}, - {L"[3]", highlight_spec_param}, // two dollar signs, so last one is not an expansion - {NULL, -1} - }; + {L"[3]", highlight_spec_param}, // two dollar signs, so last one is not an expansion + {NULL, -1}}; - const highlight_component_t *tests[] = {components1, components2, components3, components4, components5, components6, components7, components8, components9, components10, components11, components12, components13}; - for (size_t which = 0; which < sizeof tests / sizeof *tests; which++) - { + const highlight_component_t *tests[] = {components1, components2, components3, components4, + components5, components6, components7, components8, + components9, components10, components11, components12, + components13}; + for (size_t which = 0; which < sizeof tests / sizeof *tests; which++) { const highlight_component_t *components = tests[which]; - // Count how many we have + // Count how many we have. size_t component_count = 0; - while (components[component_count].txt != NULL) - { + while (components[component_count].txt != NULL) { component_count++; } - // Generate the text + // Generate the text. wcstring text; std::vector expected_colors; - for (size_t i=0; i < component_count; i++) - { - if (i > 0) - { + for (size_t i = 0; i < component_count; i++) { + if (i > 0) { text.push_back(L' '); expected_colors.push_back(0); } @@ -4057,49 +3527,42 @@ static void test_highlighting(void) std::vector colors(text.size()); highlight_shell(text, colors, 20, NULL, env_vars_snapshot_t()); - if (expected_colors.size() != colors.size()) - { - err(L"Color vector has wrong size! Expected %lu, actual %lu", expected_colors.size(), colors.size()); + if (expected_colors.size() != colors.size()) { + err(L"Color vector has wrong size! Expected %lu, actual %lu", expected_colors.size(), + colors.size()); } do_test(expected_colors.size() == colors.size()); - for (size_t i=0; i < text.size(); i++) - { + for (size_t i = 0; i < text.size(); i++) { // Hackish space handling. We don't care about the colors in spaces. - if (text.at(i) == L' ') - continue; + if (text.at(i) == L' ') continue; - if (expected_colors.at(i) != colors.at(i)) - { + if (expected_colors.at(i) != colors.at(i)) { const wcstring spaces(i, L' '); - err(L"Wrong color at index %lu in text (expected %#x, actual %#x):\n%ls\n%ls^", i, expected_colors.at(i), colors.at(i), text.c_str(), spaces.c_str()); + err(L"Wrong color at index %lu in text (expected %#x, actual %#x):\n%ls\n%ls^", i, + expected_colors.at(i), colors.at(i), text.c_str(), spaces.c_str()); } } } - if (system("rm -Rf /tmp/fish_highlight_test")) - { + if (system("rm -Rf /tmp/fish_highlight_test")) { err(L"rm failed"); } } -static void test_wcstring_tok(void) -{ +static void test_wcstring_tok(void) { say(L"Testing wcstring_tok"); wcstring buff = L"hello world"; wcstring needle = L" \t\n"; wcstring_range loc = wcstring_tok(buff, needle); - if (loc.first == wcstring::npos || buff.substr(loc.first, loc.second) != L"hello") - { + if (loc.first == wcstring::npos || buff.substr(loc.first, loc.second) != L"hello") { err(L"Wrong results from first wcstring_tok(): {%zu, %zu}", loc.first, loc.second); } loc = wcstring_tok(buff, needle, loc); - if (loc.first == wcstring::npos || buff.substr(loc.first, loc.second) != L"world") - { + if (loc.first == wcstring::npos || buff.substr(loc.first, loc.second) != L"world") { err(L"Wrong results from second wcstring_tok(): {%zu, %zu}", loc.first, loc.second); } loc = wcstring_tok(buff, needle, loc); - if (loc.first != wcstring::npos) - { + if (loc.first != wcstring::npos) { err(L"Wrong results from third wcstring_tok(): {%zu, %zu}", loc.first, loc.second); } @@ -4107,327 +3570,320 @@ static void test_wcstring_tok(void) loc = wcstring_tok(buff, needle); // loc is "hello" again loc = wcstring_tok(buff, L"", loc); - if (loc.first == wcstring::npos || buff.substr(loc.first, loc.second) != L"world") - { - err(L"Wrong results from wcstring_tok with empty needle: {%zu, %zu}", loc.first, loc.second); + if (loc.first == wcstring::npos || buff.substr(loc.first, loc.second) != L"world") { + err(L"Wrong results from wcstring_tok with empty needle: {%zu, %zu}", loc.first, + loc.second); } } int builtin_string(parser_t &parser, io_streams_t &streams, wchar_t **argv); -static void run_one_string_test(const wchar_t **argv, int expected_rc, const wchar_t *expected_out) -{ +static void run_one_string_test(const wchar_t **argv, int expected_rc, + const wchar_t *expected_out) { parser_t parser; io_streams_t streams; - streams.stdin_is_directly_redirected = false; // read from argv instead of stdin - int rc = builtin_string(parser, streams, const_cast(argv)); + streams.stdin_is_directly_redirected = false; // read from argv instead of stdin + int rc = builtin_string(parser, streams, const_cast(argv)); wcstring args; - for (int i = 0; argv[i] != 0; i++) - { + for (int i = 0; argv[i] != 0; i++) { args += escape_string(argv[i], ESCAPE_ALL) + L' '; } args.resize(args.size() - 1); - if (rc != expected_rc) - { - err(L"Test failed on line %lu: [%ls]: expected return code %d but got %d", - __LINE__, args.c_str(), expected_rc, rc); - } - else if (streams.out.buffer() != expected_out) - { - err(L"Test failed on line %lu: [%ls]: expected [%ls] but got [%ls]", - __LINE__, args.c_str(), - escape_string(expected_out, ESCAPE_ALL).c_str(), - escape_string(streams.out.buffer(), ESCAPE_ALL).c_str()); + if (rc != expected_rc) { + err(L"Test failed on line %lu: [%ls]: expected return code %d but got %d", __LINE__, + args.c_str(), expected_rc, rc); + } else if (streams.out.buffer() != expected_out) { + err(L"Test failed on line %lu: [%ls]: expected [%ls] but got [%ls]", __LINE__, args.c_str(), + escape_string(expected_out, ESCAPE_ALL).c_str(), + escape_string(streams.out.buffer(), ESCAPE_ALL).c_str()); } } -static void test_string(void) -{ - static struct string_test - { +static void test_string(void) { + static struct string_test { const wchar_t *argv[15]; int expected_rc; const wchar_t *expected_out; - } - string_tests[] = - { - { {L"string", L"escape", 0}, 1, L"" }, - { {L"string", L"escape", L"", 0}, 0, L"''\n" }, - { {L"string", L"escape", L"-n", L"", 0}, 0, L"\n" }, - { {L"string", L"escape", L"a", 0}, 0, L"a\n" }, - { {L"string", L"escape", L"\x07", 0}, 0, L"\\cg\n" }, - { {L"string", L"escape", L"\"x\"", 0}, 0, L"'\"x\"'\n" }, - { {L"string", L"escape", L"hello world", 0}, 0, L"'hello world'\n" }, - { {L"string", L"escape", L"-n", L"hello world", 0}, 0, L"hello\\ world\n" }, - { {L"string", L"escape", L"hello", L"world", 0}, 0, L"hello\nworld\n" }, - { {L"string", L"escape", L"-n", L"~", 0}, 0, L"\\~\n" }, + } string_tests[] = { + {{L"string", L"escape", 0}, 1, L""}, + {{L"string", L"escape", L"", 0}, 0, L"''\n"}, + {{L"string", L"escape", L"-n", L"", 0}, 0, L"\n"}, + {{L"string", L"escape", L"a", 0}, 0, L"a\n"}, + {{L"string", L"escape", L"\x07", 0}, 0, L"\\cg\n"}, + {{L"string", L"escape", L"\"x\"", 0}, 0, L"'\"x\"'\n"}, + {{L"string", L"escape", L"hello world", 0}, 0, L"'hello world'\n"}, + {{L"string", L"escape", L"-n", L"hello world", 0}, 0, L"hello\\ world\n"}, + {{L"string", L"escape", L"hello", L"world", 0}, 0, L"hello\nworld\n"}, + {{L"string", L"escape", L"-n", L"~", 0}, 0, L"\\~\n"}, - { {L"string", L"join", 0}, 2, L"" }, - { {L"string", L"join", L"", 0}, 1, L"" }, - { {L"string", L"join", L"", L"", L"", L"", 0}, 0, L"\n" }, - { {L"string", L"join", L"", L"a", L"b", L"c", 0}, 0, L"abc\n" }, - { {L"string", L"join", L".", L"fishshell", L"com", 0}, 0, L"fishshell.com\n" }, - { {L"string", L"join", L"/", L"usr", 0}, 1, L"usr\n" }, - { {L"string", L"join", L"/", L"usr", L"local", L"bin", 0}, 0, L"usr/local/bin\n" }, - { {L"string", L"join", L"...", L"3", L"2", L"1", 0}, 0, L"3...2...1\n" }, - { {L"string", L"join", L"-q", 0}, 2, L"" }, - { {L"string", L"join", L"-q", L".", 0}, 1, L"" }, - { {L"string", L"join", L"-q", L".", L".", 0}, 1, L"" }, + {{L"string", L"join", 0}, 2, L""}, + {{L"string", L"join", L"", 0}, 1, L""}, + {{L"string", L"join", L"", L"", L"", L"", 0}, 0, L"\n"}, + {{L"string", L"join", L"", L"a", L"b", L"c", 0}, 0, L"abc\n"}, + {{L"string", L"join", L".", L"fishshell", L"com", 0}, 0, L"fishshell.com\n"}, + {{L"string", L"join", L"/", L"usr", 0}, 1, L"usr\n"}, + {{L"string", L"join", L"/", L"usr", L"local", L"bin", 0}, 0, L"usr/local/bin\n"}, + {{L"string", L"join", L"...", L"3", L"2", L"1", 0}, 0, L"3...2...1\n"}, + {{L"string", L"join", L"-q", 0}, 2, L""}, + {{L"string", L"join", L"-q", L".", 0}, 1, L""}, + {{L"string", L"join", L"-q", L".", L".", 0}, 1, L""}, - { {L"string", L"length", 0}, 1, L"" }, - { {L"string", L"length", L"", 0}, 1, L"0\n" }, - { {L"string", L"length", L"", L"", L"", 0}, 1, L"0\n0\n0\n" }, - { {L"string", L"length", L"a", 0}, 0, L"1\n" }, - { {L"string", L"length", L"\U0002008A", 0}, 0, L"1\n" }, - { {L"string", L"length", L"um", L"dois", L"três", 0}, 0, L"2\n4\n4\n" }, - { {L"string", L"length", L"um", L"dois", L"três", 0}, 0, L"2\n4\n4\n" }, - { {L"string", L"length", L"-q", 0}, 1, L"" }, - { {L"string", L"length", L"-q", L"", 0}, 1, L"" }, - { {L"string", L"length", L"-q", L"a", 0}, 0, L"" }, + {{L"string", L"length", 0}, 1, L""}, + {{L"string", L"length", L"", 0}, 1, L"0\n"}, + {{L"string", L"length", L"", L"", L"", 0}, 1, L"0\n0\n0\n"}, + {{L"string", L"length", L"a", 0}, 0, L"1\n"}, + {{L"string", L"length", L"\U0002008A", 0}, 0, L"1\n"}, + {{L"string", L"length", L"um", L"dois", L"três", 0}, 0, L"2\n4\n4\n"}, + {{L"string", L"length", L"um", L"dois", L"três", 0}, 0, L"2\n4\n4\n"}, + {{L"string", L"length", L"-q", 0}, 1, L""}, + {{L"string", L"length", L"-q", L"", 0}, 1, L""}, + {{L"string", L"length", L"-q", L"a", 0}, 0, L""}, - { {L"string", L"match", 0}, 2, L"" }, - { {L"string", L"match", L"", 0}, 1, L"" }, - { {L"string", L"match", L"", L"", 0}, 0, L"\n" }, - { {L"string", L"match", L"?", L"a", 0}, 0, L"a\n" }, - { {L"string", L"match", L"*", L"", 0}, 0, L"\n" }, - { {L"string", L"match", L"**", L"", 0}, 0, L"\n" }, - { {L"string", L"match", L"*", L"xyzzy", 0}, 0, L"xyzzy\n" }, - { {L"string", L"match", L"**", L"plugh", 0}, 0, L"plugh\n" }, - { {L"string", L"match", L"a*b", L"axxb", 0}, 0, L"axxb\n" }, - { {L"string", L"match", L"a??b", L"axxb", 0}, 0, L"axxb\n" }, - { {L"string", L"match", L"-i", L"a??B", L"axxb", 0}, 0, L"axxb\n" }, - { {L"string", L"match", L"-i", L"a??b", L"Axxb", 0}, 0, L"Axxb\n" }, - { {L"string", L"match", L"a*", L"axxb", 0}, 0, L"axxb\n" }, - { {L"string", L"match", L"*a", L"xxa", 0}, 0, L"xxa\n" }, - { {L"string", L"match", L"*a*", L"axa", 0}, 0, L"axa\n" }, - { {L"string", L"match", L"*a*", L"xax", 0}, 0, L"xax\n" }, - { {L"string", L"match", L"*a*", L"bxa", 0}, 0, L"bxa\n" }, - { {L"string", L"match", L"*a", L"a", 0}, 0, L"a\n" }, - { {L"string", L"match", L"a*", L"a", 0}, 0, L"a\n" }, - { {L"string", L"match", L"a*b*c", L"axxbyyc", 0}, 0, L"axxbyyc\n" }, - { {L"string", L"match", L"a*b?c", L"axxbyc", 0}, 0, L"axxbyc\n" }, - { {L"string", L"match", L"*?", L"a", 0}, 0, L"a\n" }, - { {L"string", L"match", L"*?", L"ab", 0}, 0, L"ab\n" }, - { {L"string", L"match", L"?*", L"a", 0}, 0, L"a\n" }, - { {L"string", L"match", L"?*", L"ab", 0}, 0, L"ab\n" }, - { {L"string", L"match", L"\\*", L"*", 0}, 0, L"*\n" }, - { {L"string", L"match", L"a*\\", L"abc\\", 0}, 0, L"abc\\\n" }, - { {L"string", L"match", L"a*\\?", L"abc?", 0}, 0, L"abc?\n" }, + {{L"string", L"match", 0}, 2, L""}, + {{L"string", L"match", L"", 0}, 1, L""}, + {{L"string", L"match", L"", L"", 0}, 0, L"\n"}, + {{L"string", L"match", L"?", L"a", 0}, 0, L"a\n"}, + {{L"string", L"match", L"*", L"", 0}, 0, L"\n"}, + {{L"string", L"match", L"**", L"", 0}, 0, L"\n"}, + {{L"string", L"match", L"*", L"xyzzy", 0}, 0, L"xyzzy\n"}, + {{L"string", L"match", L"**", L"plugh", 0}, 0, L"plugh\n"}, + {{L"string", L"match", L"a*b", L"axxb", 0}, 0, L"axxb\n"}, + {{L"string", L"match", L"a??b", L"axxb", 0}, 0, L"axxb\n"}, + {{L"string", L"match", L"-i", L"a??B", L"axxb", 0}, 0, L"axxb\n"}, + {{L"string", L"match", L"-i", L"a??b", L"Axxb", 0}, 0, L"Axxb\n"}, + {{L"string", L"match", L"a*", L"axxb", 0}, 0, L"axxb\n"}, + {{L"string", L"match", L"*a", L"xxa", 0}, 0, L"xxa\n"}, + {{L"string", L"match", L"*a*", L"axa", 0}, 0, L"axa\n"}, + {{L"string", L"match", L"*a*", L"xax", 0}, 0, L"xax\n"}, + {{L"string", L"match", L"*a*", L"bxa", 0}, 0, L"bxa\n"}, + {{L"string", L"match", L"*a", L"a", 0}, 0, L"a\n"}, + {{L"string", L"match", L"a*", L"a", 0}, 0, L"a\n"}, + {{L"string", L"match", L"a*b*c", L"axxbyyc", 0}, 0, L"axxbyyc\n"}, + {{L"string", L"match", L"a*b?c", L"axxbyc", 0}, 0, L"axxbyc\n"}, + {{L"string", L"match", L"*?", L"a", 0}, 0, L"a\n"}, + {{L"string", L"match", L"*?", L"ab", 0}, 0, L"ab\n"}, + {{L"string", L"match", L"?*", L"a", 0}, 0, L"a\n"}, + {{L"string", L"match", L"?*", L"ab", 0}, 0, L"ab\n"}, + {{L"string", L"match", L"\\*", L"*", 0}, 0, L"*\n"}, + {{L"string", L"match", L"a*\\", L"abc\\", 0}, 0, L"abc\\\n"}, + {{L"string", L"match", L"a*\\?", L"abc?", 0}, 0, L"abc?\n"}, - { {L"string", L"match", L"?", L"", 0}, 1, L"" }, - { {L"string", L"match", L"?", L"ab", 0}, 1, L"" }, - { {L"string", L"match", L"??", L"a", 0}, 1, L"" }, - { {L"string", L"match", L"?a", L"a", 0}, 1, L"" }, - { {L"string", L"match", L"a?", L"a", 0}, 1, L"" }, - { {L"string", L"match", L"a??B", L"axxb", 0}, 1, L"" }, - { {L"string", L"match", L"a*b", L"axxbc", 0}, 1, L"" }, - { {L"string", L"match", L"*b", L"bbba", 0}, 1, L"" }, - { {L"string", L"match", L"0x[0-9a-fA-F][0-9a-fA-F]", L"0xbad", 0}, 1, L"" }, + {{L"string", L"match", L"?", L"", 0}, 1, L""}, + {{L"string", L"match", L"?", L"ab", 0}, 1, L""}, + {{L"string", L"match", L"??", L"a", 0}, 1, L""}, + {{L"string", L"match", L"?a", L"a", 0}, 1, L""}, + {{L"string", L"match", L"a?", L"a", 0}, 1, L""}, + {{L"string", L"match", L"a??B", L"axxb", 0}, 1, L""}, + {{L"string", L"match", L"a*b", L"axxbc", 0}, 1, L""}, + {{L"string", L"match", L"*b", L"bbba", 0}, 1, L""}, + {{L"string", L"match", L"0x[0-9a-fA-F][0-9a-fA-F]", L"0xbad", 0}, 1, L""}, - { {L"string", L"match", L"-a", L"*", L"ab", L"cde", 0}, 0, L"ab\ncde\n" }, - { {L"string", L"match", L"*", L"ab", L"cde", 0}, 0, L"ab\ncde\n" }, - { {L"string", L"match", L"-n", L"*d*", L"cde", 0}, 0, L"1 3\n" }, - { {L"string", L"match", L"-n", L"*x*", L"cde", 0}, 1, L"" }, - { {L"string", L"match", L"-q", L"a*", L"b", L"c", 0}, 1, L"" }, - { {L"string", L"match", L"-q", L"a*", L"b", L"a", 0}, 0, L"" }, + {{L"string", L"match", L"-a", L"*", L"ab", L"cde", 0}, 0, L"ab\ncde\n"}, + {{L"string", L"match", L"*", L"ab", L"cde", 0}, 0, L"ab\ncde\n"}, + {{L"string", L"match", L"-n", L"*d*", L"cde", 0}, 0, L"1 3\n"}, + {{L"string", L"match", L"-n", L"*x*", L"cde", 0}, 1, L""}, + {{L"string", L"match", L"-q", L"a*", L"b", L"c", 0}, 1, L""}, + {{L"string", L"match", L"-q", L"a*", L"b", L"a", 0}, 0, L""}, - { {L"string", L"match", L"-r", 0}, 2, L"" }, - { {L"string", L"match", L"-r", L"", 0}, 1, L"" }, - { {L"string", L"match", L"-r", L"", L"", 0}, 0, L"\n" }, - { {L"string", L"match", L"-r", L".", L"a", 0}, 0, L"a\n" }, - { {L"string", L"match", L"-r", L".*", L"", 0}, 0, L"\n" }, - { {L"string", L"match", L"-r", L"a*b", L"b", 0}, 0, L"b\n" }, - { {L"string", L"match", L"-r", L"a*b", L"aab", 0}, 0, L"aab\n" }, - { {L"string", L"match", L"-r", L"-i", L"a*b", L"Aab", 0}, 0, L"Aab\n" }, - { {L"string", L"match", L"-r", L"-a", L"a[bc]", L"abadac", 0}, 0, L"ab\nac\n" }, - { {L"string", L"match", L"-r", L"a", L"xaxa", L"axax", 0}, 0, L"a\na\n" }, - { {L"string", L"match", L"-r", L"-a", L"a", L"xaxa", L"axax", 0}, 0, L"a\na\na\na\n" }, - { {L"string", L"match", L"-r", L"a[bc]", L"abadac", 0}, 0, L"ab\n" }, - { {L"string", L"match", L"-r", L"-q", L"a[bc]", L"abadac", 0}, 0, L"" }, - { {L"string", L"match", L"-r", L"-q", L"a[bc]", L"ad", 0}, 1, L"" }, - { {L"string", L"match", L"-r", L"(a+)b(c)", L"aabc", 0}, 0, L"aabc\naa\nc\n" }, - { {L"string", L"match", L"-r", L"-a", L"(a)b(c)", L"abcabc", 0}, 0, L"abc\na\nc\nabc\na\nc\n" }, - { {L"string", L"match", L"-r", L"(a)b(c)", L"abcabc", 0}, 0, L"abc\na\nc\n" }, - { {L"string", L"match", L"-r", L"(a|(z))(bc)", L"abc", 0}, 0, L"abc\na\nbc\n" }, - { {L"string", L"match", L"-r", L"-n", L"a", L"ada", L"dad", 0}, 0, L"1 1\n2 1\n" }, - { {L"string", L"match", L"-r", L"-n", L"-a", L"a", L"bacadae", 0}, 0, L"2 1\n4 1\n6 1\n" }, - { {L"string", L"match", L"-r", L"-n", L"(a).*(b)", L"a---b", 0}, 0, L"1 5\n1 1\n5 1\n" }, - { {L"string", L"match", L"-r", L"-n", L"(a)(b)", L"ab", 0}, 0, L"1 2\n1 1\n2 1\n" }, - { {L"string", L"match", L"-r", L"-n", L"(a)(b)", L"abab", 0}, 0, L"1 2\n1 1\n2 1\n" }, - { {L"string", L"match", L"-r", L"-n", L"-a", L"(a)(b)", L"abab", 0}, 0, L"1 2\n1 1\n2 1\n3 2\n3 1\n4 1\n" }, - { {L"string", L"match", L"-r", L"*", L"", 0}, 2, L"" }, - { {L"string", L"match", L"-r", L"-a", L"a*", L"b", 0}, 0, L"\n\n" }, - { {L"string", L"match", L"-r", L"foo\\Kbar", L"foobar", 0}, 0, L"bar\n" }, - { {L"string", L"match", L"-r", L"(foo)\\Kbar", L"foobar", 0}, 0, L"bar\nfoo\n" }, - { {L"string", L"match", L"-r", L"(?=ab\\K)", L"ab", 0}, 0, L"\n" }, - { {L"string", L"match", L"-r", L"(?=ab\\K)..(?=cd\\K)", L"abcd", 0}, 0, L"\n" }, + {{L"string", L"match", L"-r", 0}, 2, L""}, + {{L"string", L"match", L"-r", L"", 0}, 1, L""}, + {{L"string", L"match", L"-r", L"", L"", 0}, 0, L"\n"}, + {{L"string", L"match", L"-r", L".", L"a", 0}, 0, L"a\n"}, + {{L"string", L"match", L"-r", L".*", L"", 0}, 0, L"\n"}, + {{L"string", L"match", L"-r", L"a*b", L"b", 0}, 0, L"b\n"}, + {{L"string", L"match", L"-r", L"a*b", L"aab", 0}, 0, L"aab\n"}, + {{L"string", L"match", L"-r", L"-i", L"a*b", L"Aab", 0}, 0, L"Aab\n"}, + {{L"string", L"match", L"-r", L"-a", L"a[bc]", L"abadac", 0}, 0, L"ab\nac\n"}, + {{L"string", L"match", L"-r", L"a", L"xaxa", L"axax", 0}, 0, L"a\na\n"}, + {{L"string", L"match", L"-r", L"-a", L"a", L"xaxa", L"axax", 0}, 0, L"a\na\na\na\n"}, + {{L"string", L"match", L"-r", L"a[bc]", L"abadac", 0}, 0, L"ab\n"}, + {{L"string", L"match", L"-r", L"-q", L"a[bc]", L"abadac", 0}, 0, L""}, + {{L"string", L"match", L"-r", L"-q", L"a[bc]", L"ad", 0}, 1, L""}, + {{L"string", L"match", L"-r", L"(a+)b(c)", L"aabc", 0}, 0, L"aabc\naa\nc\n"}, + {{L"string", L"match", L"-r", L"-a", L"(a)b(c)", L"abcabc", 0}, + 0, + L"abc\na\nc\nabc\na\nc\n"}, + {{L"string", L"match", L"-r", L"(a)b(c)", L"abcabc", 0}, 0, L"abc\na\nc\n"}, + {{L"string", L"match", L"-r", L"(a|(z))(bc)", L"abc", 0}, 0, L"abc\na\nbc\n"}, + {{L"string", L"match", L"-r", L"-n", L"a", L"ada", L"dad", 0}, 0, L"1 1\n2 1\n"}, + {{L"string", L"match", L"-r", L"-n", L"-a", L"a", L"bacadae", 0}, 0, L"2 1\n4 1\n6 1\n"}, + {{L"string", L"match", L"-r", L"-n", L"(a).*(b)", L"a---b", 0}, 0, L"1 5\n1 1\n5 1\n"}, + {{L"string", L"match", L"-r", L"-n", L"(a)(b)", L"ab", 0}, 0, L"1 2\n1 1\n2 1\n"}, + {{L"string", L"match", L"-r", L"-n", L"(a)(b)", L"abab", 0}, 0, L"1 2\n1 1\n2 1\n"}, + {{L"string", L"match", L"-r", L"-n", L"-a", L"(a)(b)", L"abab", 0}, + 0, + L"1 2\n1 1\n2 1\n3 2\n3 1\n4 1\n"}, + {{L"string", L"match", L"-r", L"*", L"", 0}, 2, L""}, + {{L"string", L"match", L"-r", L"-a", L"a*", L"b", 0}, 0, L"\n\n"}, + {{L"string", L"match", L"-r", L"foo\\Kbar", L"foobar", 0}, 0, L"bar\n"}, + {{L"string", L"match", L"-r", L"(foo)\\Kbar", L"foobar", 0}, 0, L"bar\nfoo\n"}, + {{L"string", L"match", L"-r", L"(?=ab\\K)", L"ab", 0}, 0, L"\n"}, + {{L"string", L"match", L"-r", L"(?=ab\\K)..(?=cd\\K)", L"abcd", 0}, 0, L"\n"}, - { {L"string", L"replace", 0}, 2, L"" }, - { {L"string", L"replace", L"", 0}, 2, L"" }, - { {L"string", L"replace", L"", L"", 0}, 1, L"" }, - { {L"string", L"replace", L"", L"", L"", 0}, 1, L"\n" }, - { {L"string", L"replace", L"", L"", L" ", 0}, 1, L" \n" }, - { {L"string", L"replace", L"a", L"b", L"", 0}, 1, L"\n" }, - { {L"string", L"replace", L"a", L"b", L"a", 0}, 0, L"b\n" }, - { {L"string", L"replace", L"a", L"b", L"xax", 0}, 0, L"xbx\n" }, - { {L"string", L"replace", L"a", L"b", L"xax", L"axa", 0}, 0, L"xbx\nbxa\n" }, - { {L"string", L"replace", L"bar", L"x", L"red barn", 0}, 0, L"red xn\n" }, - { {L"string", L"replace", L"x", L"bar", L"red xn", 0}, 0, L"red barn\n" }, - { {L"string", L"replace", L"--", L"x", L"-", L"xyz", 0}, 0, L"-yz\n" }, - { {L"string", L"replace", L"--", L"y", L"-", L"xyz", 0}, 0, L"x-z\n" }, - { {L"string", L"replace", L"--", L"z", L"-", L"xyz", 0}, 0, L"xy-\n" }, - { {L"string", L"replace", L"-i", L"z", L"X", L"_Z_", 0}, 0, L"_X_\n" }, - { {L"string", L"replace", L"-a", L"a", L"A", L"aaa", 0}, 0, L"AAA\n" }, - { {L"string", L"replace", L"-i", L"a", L"z", L"AAA", 0}, 0, L"zAA\n" }, - { {L"string", L"replace", L"-q", L"x", L">x<", L"x", 0}, 0, L"" }, - { {L"string", L"replace", L"-a", L"x", L"", L"xxx", 0}, 0, L"\n" }, - { {L"string", L"replace", L"-a", L"***", L"_", L"*****", 0}, 0, L"_**\n" }, - { {L"string", L"replace", L"-a", L"***", L"***", L"******", 0}, 0, L"******\n" }, - { {L"string", L"replace", L"-a", L"a", L"b", L"xax", L"axa", 0}, 0, L"xbx\nbxb\n" }, + {{L"string", L"replace", 0}, 2, L""}, + {{L"string", L"replace", L"", 0}, 2, L""}, + {{L"string", L"replace", L"", L"", 0}, 1, L""}, + {{L"string", L"replace", L"", L"", L"", 0}, 1, L"\n"}, + {{L"string", L"replace", L"", L"", L" ", 0}, 1, L" \n"}, + {{L"string", L"replace", L"a", L"b", L"", 0}, 1, L"\n"}, + {{L"string", L"replace", L"a", L"b", L"a", 0}, 0, L"b\n"}, + {{L"string", L"replace", L"a", L"b", L"xax", 0}, 0, L"xbx\n"}, + {{L"string", L"replace", L"a", L"b", L"xax", L"axa", 0}, 0, L"xbx\nbxa\n"}, + {{L"string", L"replace", L"bar", L"x", L"red barn", 0}, 0, L"red xn\n"}, + {{L"string", L"replace", L"x", L"bar", L"red xn", 0}, 0, L"red barn\n"}, + {{L"string", L"replace", L"--", L"x", L"-", L"xyz", 0}, 0, L"-yz\n"}, + {{L"string", L"replace", L"--", L"y", L"-", L"xyz", 0}, 0, L"x-z\n"}, + {{L"string", L"replace", L"--", L"z", L"-", L"xyz", 0}, 0, L"xy-\n"}, + {{L"string", L"replace", L"-i", L"z", L"X", L"_Z_", 0}, 0, L"_X_\n"}, + {{L"string", L"replace", L"-a", L"a", L"A", L"aaa", 0}, 0, L"AAA\n"}, + {{L"string", L"replace", L"-i", L"a", L"z", L"AAA", 0}, 0, L"zAA\n"}, + {{L"string", L"replace", L"-q", L"x", L">x<", L"x", 0}, 0, L""}, + {{L"string", L"replace", L"-a", L"x", L"", L"xxx", 0}, 0, L"\n"}, + {{L"string", L"replace", L"-a", L"***", L"_", L"*****", 0}, 0, L"_**\n"}, + {{L"string", L"replace", L"-a", L"***", L"***", L"******", 0}, 0, L"******\n"}, + {{L"string", L"replace", L"-a", L"a", L"b", L"xax", L"axa", 0}, 0, L"xbx\nbxb\n"}, - { {L"string", L"replace", L"-r", 0}, 2, L"" }, - { {L"string", L"replace", L"-r", L"", 0}, 2, L"" }, - { {L"string", L"replace", L"-r", L"", L"", 0}, 1, L"" }, - { {L"string", L"replace", L"-r", L"", L"", L"", 0}, 0, L"\n" }, // pcre2 behavior - { {L"string", L"replace", L"-r", L"", L"", L" ", 0}, 0, L" \n" }, // pcre2 behavior - { {L"string", L"replace", L"-r", L"a", L"b", L"", 0}, 1, L"\n" }, - { {L"string", L"replace", L"-r", L"a", L"b", L"a", 0}, 0, L"b\n" }, - { {L"string", L"replace", L"-r", L".", L"x", L"abc", 0}, 0, L"xbc\n" }, - { {L"string", L"replace", L"-r", L".", L"", L"abc", 0}, 0, L"bc\n" }, - { {L"string", L"replace", L"-r", L"(\\w)(\\w)", L"$2$1", L"ab", 0}, 0, L"ba\n" }, - { {L"string", L"replace", L"-r", L"(\\w)", L"$1$1", L"ab", 0}, 0, L"aab\n" }, - { {L"string", L"replace", L"-r", L"-a", L".", L"x", L"abc", 0}, 0, L"xxx\n" }, - { {L"string", L"replace", L"-r", L"-a", L"(\\w)", L"$1$1", L"ab", 0}, 0, L"aabb\n" }, - { {L"string", L"replace", L"-r", L"-a", L".", L"", L"abc", 0}, 0, L"\n" }, - { {L"string", L"replace", L"-r", L"a", L"x", L"bc", L"cd", L"de", 0}, 1, L"bc\ncd\nde\n" }, - { {L"string", L"replace", L"-r", L"a", L"x", L"aba", L"caa", 0}, 0, L"xba\ncxa\n" }, - { {L"string", L"replace", L"-r", L"-a", L"a", L"x", L"aba", L"caa", 0}, 0, L"xbx\ncxx\n" }, - { {L"string", L"replace", L"-r", L"-i", L"A", L"b", L"xax", 0}, 0, L"xbx\n" }, - { {L"string", L"replace", L"-r", L"-i", L"[a-z]", L".", L"1A2B", 0}, 0, L"1.2B\n" }, - { {L"string", L"replace", L"-r", L"A", L"b", L"xax", 0}, 1, L"xax\n" }, - { {L"string", L"replace", L"-r", L"a", L"$1", L"a", 0}, 2, L"" }, - { {L"string", L"replace", L"-r", L"(a)", L"$2", L"a", 0}, 2, L"" }, - { {L"string", L"replace", L"-r", L"*", L".", L"a", 0}, 2, L"" }, - { {L"string", L"replace", L"-r", L"^(.)", L"\t$1", L"abc", L"x", 0}, 0, L"\tabc\n\tx\n" }, + {{L"string", L"replace", L"-r", 0}, 2, L""}, + {{L"string", L"replace", L"-r", L"", 0}, 2, L""}, + {{L"string", L"replace", L"-r", L"", L"", 0}, 1, L""}, + {{L"string", L"replace", L"-r", L"", L"", L"", 0}, 0, L"\n"}, // pcre2 behavior + {{L"string", L"replace", L"-r", L"", L"", L" ", 0}, 0, L" \n"}, // pcre2 behavior + {{L"string", L"replace", L"-r", L"a", L"b", L"", 0}, 1, L"\n"}, + {{L"string", L"replace", L"-r", L"a", L"b", L"a", 0}, 0, L"b\n"}, + {{L"string", L"replace", L"-r", L".", L"x", L"abc", 0}, 0, L"xbc\n"}, + {{L"string", L"replace", L"-r", L".", L"", L"abc", 0}, 0, L"bc\n"}, + {{L"string", L"replace", L"-r", L"(\\w)(\\w)", L"$2$1", L"ab", 0}, 0, L"ba\n"}, + {{L"string", L"replace", L"-r", L"(\\w)", L"$1$1", L"ab", 0}, 0, L"aab\n"}, + {{L"string", L"replace", L"-r", L"-a", L".", L"x", L"abc", 0}, 0, L"xxx\n"}, + {{L"string", L"replace", L"-r", L"-a", L"(\\w)", L"$1$1", L"ab", 0}, 0, L"aabb\n"}, + {{L"string", L"replace", L"-r", L"-a", L".", L"", L"abc", 0}, 0, L"\n"}, + {{L"string", L"replace", L"-r", L"a", L"x", L"bc", L"cd", L"de", 0}, 1, L"bc\ncd\nde\n"}, + {{L"string", L"replace", L"-r", L"a", L"x", L"aba", L"caa", 0}, 0, L"xba\ncxa\n"}, + {{L"string", L"replace", L"-r", L"-a", L"a", L"x", L"aba", L"caa", 0}, 0, L"xbx\ncxx\n"}, + {{L"string", L"replace", L"-r", L"-i", L"A", L"b", L"xax", 0}, 0, L"xbx\n"}, + {{L"string", L"replace", L"-r", L"-i", L"[a-z]", L".", L"1A2B", 0}, 0, L"1.2B\n"}, + {{L"string", L"replace", L"-r", L"A", L"b", L"xax", 0}, 1, L"xax\n"}, + {{L"string", L"replace", L"-r", L"a", L"$1", L"a", 0}, 2, L""}, + {{L"string", L"replace", L"-r", L"(a)", L"$2", L"a", 0}, 2, L""}, + {{L"string", L"replace", L"-r", L"*", L".", L"a", 0}, 2, L""}, + {{L"string", L"replace", L"-r", L"^(.)", L"\t$1", L"abc", L"x", 0}, 0, L"\tabc\n\tx\n"}, - { {L"string", L"split", 0}, 2, L"" }, - { {L"string", L"split", L":", 0}, 1, L"" }, - { {L"string", L"split", L".", L"www.ch.ic.ac.uk", 0}, 0, L"www\nch\nic\nac\nuk\n" }, - { {L"string", L"split", L"..", L"....", 0}, 0, L"\n\n\n" }, - { {L"string", L"split", L"-m", L"x", L"..", L"....", 0}, 2, L"" }, - { {L"string", L"split", L"-m1", L"..", L"....", 0}, 0, L"\n..\n" }, - { {L"string", L"split", L"-m0", L"/", L"/usr/local/bin/fish", 0}, 1, L"/usr/local/bin/fish\n" }, - { {L"string", L"split", L"-m2", L":", L"a:b:c:d", L"e:f:g:h", 0}, 0, L"a\nb\nc:d\ne\nf\ng:h\n" }, - { {L"string", L"split", L"-m1", L"-r", L"/", L"/usr/local/bin/fish", 0}, 0, L"/usr/local/bin\nfish\n" }, - { {L"string", L"split", L"-r", L".", L"www.ch.ic.ac.uk", 0}, 0, L"www\nch\nic\nac\nuk\n" }, - { {L"string", L"split", L"--", L"--", L"a--b---c----d", 0}, 0, L"a\nb\n-c\n\nd\n" }, - { {L"string", L"split", L"-r", L"..", L"....", 0}, 0, L"\n\n\n" }, - { {L"string", L"split", L"-r", L"--", L"--", L"a--b---c----d", 0}, 0, L"a\nb-\nc\n\nd\n" }, - { {L"string", L"split", L"", L"", 0}, 1, L"\n" }, - { {L"string", L"split", L"", L"a", 0}, 1, L"a\n" }, - { {L"string", L"split", L"", L"ab", 0}, 0, L"a\nb\n" }, - { {L"string", L"split", L"", L"abc", 0}, 0, L"a\nb\nc\n" }, - { {L"string", L"split", L"-m1", L"", L"abc", 0}, 0, L"a\nbc\n" }, - { {L"string", L"split", L"-r", L"", L"", 0}, 1, L"\n" }, - { {L"string", L"split", L"-r", L"", L"a", 0}, 1, L"a\n" }, - { {L"string", L"split", L"-r", L"", L"ab", 0}, 0, L"a\nb\n" }, - { {L"string", L"split", L"-r", L"", L"abc", 0}, 0, L"a\nb\nc\n" }, - { {L"string", L"split", L"-r", L"-m1", L"", L"abc", 0}, 0, L"ab\nc\n" }, - { {L"string", L"split", L"-q", 0}, 2, L"" }, - { {L"string", L"split", L"-q", L":", 0}, 1, L"" }, - { {L"string", L"split", L"-q", L"x", L"axbxc", 0}, 0, L"" }, + {{L"string", L"split", 0}, 2, L""}, + {{L"string", L"split", L":", 0}, 1, L""}, + {{L"string", L"split", L".", L"www.ch.ic.ac.uk", 0}, 0, L"www\nch\nic\nac\nuk\n"}, + {{L"string", L"split", L"..", L"....", 0}, 0, L"\n\n\n"}, + {{L"string", L"split", L"-m", L"x", L"..", L"....", 0}, 2, L""}, + {{L"string", L"split", L"-m1", L"..", L"....", 0}, 0, L"\n..\n"}, + {{L"string", L"split", L"-m0", L"/", L"/usr/local/bin/fish", 0}, + 1, + L"/usr/local/bin/fish\n"}, + {{L"string", L"split", L"-m2", L":", L"a:b:c:d", L"e:f:g:h", 0}, + 0, + L"a\nb\nc:d\ne\nf\ng:h\n"}, + {{L"string", L"split", L"-m1", L"-r", L"/", L"/usr/local/bin/fish", 0}, + 0, + L"/usr/local/bin\nfish\n"}, + {{L"string", L"split", L"-r", L".", L"www.ch.ic.ac.uk", 0}, 0, L"www\nch\nic\nac\nuk\n"}, + {{L"string", L"split", L"--", L"--", L"a--b---c----d", 0}, 0, L"a\nb\n-c\n\nd\n"}, + {{L"string", L"split", L"-r", L"..", L"....", 0}, 0, L"\n\n\n"}, + {{L"string", L"split", L"-r", L"--", L"--", L"a--b---c----d", 0}, 0, L"a\nb-\nc\n\nd\n"}, + {{L"string", L"split", L"", L"", 0}, 1, L"\n"}, + {{L"string", L"split", L"", L"a", 0}, 1, L"a\n"}, + {{L"string", L"split", L"", L"ab", 0}, 0, L"a\nb\n"}, + {{L"string", L"split", L"", L"abc", 0}, 0, L"a\nb\nc\n"}, + {{L"string", L"split", L"-m1", L"", L"abc", 0}, 0, L"a\nbc\n"}, + {{L"string", L"split", L"-r", L"", L"", 0}, 1, L"\n"}, + {{L"string", L"split", L"-r", L"", L"a", 0}, 1, L"a\n"}, + {{L"string", L"split", L"-r", L"", L"ab", 0}, 0, L"a\nb\n"}, + {{L"string", L"split", L"-r", L"", L"abc", 0}, 0, L"a\nb\nc\n"}, + {{L"string", L"split", L"-r", L"-m1", L"", L"abc", 0}, 0, L"ab\nc\n"}, + {{L"string", L"split", L"-q", 0}, 2, L""}, + {{L"string", L"split", L"-q", L":", 0}, 1, L""}, + {{L"string", L"split", L"-q", L"x", L"axbxc", 0}, 0, L""}, - { {L"string", L"sub", 0}, 1, L"" }, - { {L"string", L"sub", L"abcde", 0}, 0, L"abcde\n"}, - { {L"string", L"sub", L"-l", L"x", L"abcde", 0}, 2, L""}, - { {L"string", L"sub", L"-s", L"x", L"abcde", 0}, 2, L""}, - { {L"string", L"sub", L"-l0", L"abcde", 0}, 0, L"\n"}, - { {L"string", L"sub", L"-l2", L"abcde", 0}, 0, L"ab\n"}, - { {L"string", L"sub", L"-l5", L"abcde", 0}, 0, L"abcde\n"}, - { {L"string", L"sub", L"-l6", L"abcde", 0}, 0, L"abcde\n"}, - { {L"string", L"sub", L"-l-1", L"abcde", 0}, 2, L""}, - { {L"string", L"sub", L"-s0", L"abcde", 0}, 2, L""}, - { {L"string", L"sub", L"-s1", L"abcde", 0}, 0, L"abcde\n"}, - { {L"string", L"sub", L"-s5", L"abcde", 0}, 0, L"e\n"}, - { {L"string", L"sub", L"-s6", L"abcde", 0}, 0, L"\n"}, - { {L"string", L"sub", L"-s-1", L"abcde", 0}, 0, L"e\n"}, - { {L"string", L"sub", L"-s-5", L"abcde", 0}, 0, L"abcde\n"}, - { {L"string", L"sub", L"-s-6", L"abcde", 0}, 0, L"abcde\n"}, - { {L"string", L"sub", L"-s1", L"-l0", L"abcde", 0}, 0, L"\n"}, - { {L"string", L"sub", L"-s1", L"-l1", L"abcde", 0}, 0, L"a\n"}, - { {L"string", L"sub", L"-s2", L"-l2", L"abcde", 0}, 0, L"bc\n"}, - { {L"string", L"sub", L"-s-1", L"-l1", L"abcde", 0}, 0, L"e\n"}, - { {L"string", L"sub", L"-s-1", L"-l2", L"abcde", 0}, 0, L"e\n"}, - { {L"string", L"sub", L"-s-3", L"-l2", L"abcde", 0}, 0, L"cd\n"}, - { {L"string", L"sub", L"-s-3", L"-l4", L"abcde", 0}, 0, L"cde\n"}, - { {L"string", L"sub", L"-q", 0}, 1, L"" }, - { {L"string", L"sub", L"-q", L"abcde", 0}, 0, L""}, + {{L"string", L"sub", 0}, 1, L""}, + {{L"string", L"sub", L"abcde", 0}, 0, L"abcde\n"}, + {{L"string", L"sub", L"-l", L"x", L"abcde", 0}, 2, L""}, + {{L"string", L"sub", L"-s", L"x", L"abcde", 0}, 2, L""}, + {{L"string", L"sub", L"-l0", L"abcde", 0}, 0, L"\n"}, + {{L"string", L"sub", L"-l2", L"abcde", 0}, 0, L"ab\n"}, + {{L"string", L"sub", L"-l5", L"abcde", 0}, 0, L"abcde\n"}, + {{L"string", L"sub", L"-l6", L"abcde", 0}, 0, L"abcde\n"}, + {{L"string", L"sub", L"-l-1", L"abcde", 0}, 2, L""}, + {{L"string", L"sub", L"-s0", L"abcde", 0}, 2, L""}, + {{L"string", L"sub", L"-s1", L"abcde", 0}, 0, L"abcde\n"}, + {{L"string", L"sub", L"-s5", L"abcde", 0}, 0, L"e\n"}, + {{L"string", L"sub", L"-s6", L"abcde", 0}, 0, L"\n"}, + {{L"string", L"sub", L"-s-1", L"abcde", 0}, 0, L"e\n"}, + {{L"string", L"sub", L"-s-5", L"abcde", 0}, 0, L"abcde\n"}, + {{L"string", L"sub", L"-s-6", L"abcde", 0}, 0, L"abcde\n"}, + {{L"string", L"sub", L"-s1", L"-l0", L"abcde", 0}, 0, L"\n"}, + {{L"string", L"sub", L"-s1", L"-l1", L"abcde", 0}, 0, L"a\n"}, + {{L"string", L"sub", L"-s2", L"-l2", L"abcde", 0}, 0, L"bc\n"}, + {{L"string", L"sub", L"-s-1", L"-l1", L"abcde", 0}, 0, L"e\n"}, + {{L"string", L"sub", L"-s-1", L"-l2", L"abcde", 0}, 0, L"e\n"}, + {{L"string", L"sub", L"-s-3", L"-l2", L"abcde", 0}, 0, L"cd\n"}, + {{L"string", L"sub", L"-s-3", L"-l4", L"abcde", 0}, 0, L"cde\n"}, + {{L"string", L"sub", L"-q", 0}, 1, L""}, + {{L"string", L"sub", L"-q", L"abcde", 0}, 0, L""}, - { {L"string", L"trim", 0}, 1, L""}, - { {L"string", L"trim", L""}, 1, L"\n"}, - { {L"string", L"trim", L" "}, 0, L"\n"}, - { {L"string", L"trim", L" \f\n\r\t"}, 0, L"\n"}, - { {L"string", L"trim", L" a"}, 0, L"a\n"}, - { {L"string", L"trim", L"a "}, 0, L"a\n"}, - { {L"string", L"trim", L" a "}, 0, L"a\n"}, - { {L"string", L"trim", L"-l", L" a"}, 0, L"a\n"}, - { {L"string", L"trim", L"-l", L"a "}, 1, L"a \n"}, - { {L"string", L"trim", L"-l", L" a "}, 0, L"a \n"}, - { {L"string", L"trim", L"-r", L" a"}, 1, L" a\n"}, - { {L"string", L"trim", L"-r", L"a "}, 0, L"a\n"}, - { {L"string", L"trim", L"-r", L" a "}, 0, L" a\n"}, - { {L"string", L"trim", L"-c", L".", L" a"}, 1, L" a\n"}, - { {L"string", L"trim", L"-c", L".", L"a "}, 1, L"a \n"}, - { {L"string", L"trim", L"-c", L".", L" a "}, 1, L" a \n"}, - { {L"string", L"trim", L"-c", L".", L".a"}, 0, L"a\n"}, - { {L"string", L"trim", L"-c", L".", L"a."}, 0, L"a\n"}, - { {L"string", L"trim", L"-c", L".", L".a."}, 0, L"a\n"}, - { {L"string", L"trim", L"-c", L"\\/", L"/a\\"}, 0, L"a\n"}, - { {L"string", L"trim", L"-c", L"\\/", L"a/"}, 0, L"a\n"}, - { {L"string", L"trim", L"-c", L"\\/", L"\\a/"}, 0, L"a\n"}, - { {L"string", L"trim", L"-c", L"", L".a."}, 1, L".a.\n"}, + {{L"string", L"trim", 0}, 1, L""}, + {{L"string", L"trim", L""}, 1, L"\n"}, + {{L"string", L"trim", L" "}, 0, L"\n"}, + {{L"string", L"trim", L" \f\n\r\t"}, 0, L"\n"}, + {{L"string", L"trim", L" a"}, 0, L"a\n"}, + {{L"string", L"trim", L"a "}, 0, L"a\n"}, + {{L"string", L"trim", L" a "}, 0, L"a\n"}, + {{L"string", L"trim", L"-l", L" a"}, 0, L"a\n"}, + {{L"string", L"trim", L"-l", L"a "}, 1, L"a \n"}, + {{L"string", L"trim", L"-l", L" a "}, 0, L"a \n"}, + {{L"string", L"trim", L"-r", L" a"}, 1, L" a\n"}, + {{L"string", L"trim", L"-r", L"a "}, 0, L"a\n"}, + {{L"string", L"trim", L"-r", L" a "}, 0, L" a\n"}, + {{L"string", L"trim", L"-c", L".", L" a"}, 1, L" a\n"}, + {{L"string", L"trim", L"-c", L".", L"a "}, 1, L"a \n"}, + {{L"string", L"trim", L"-c", L".", L" a "}, 1, L" a \n"}, + {{L"string", L"trim", L"-c", L".", L".a"}, 0, L"a\n"}, + {{L"string", L"trim", L"-c", L".", L"a."}, 0, L"a\n"}, + {{L"string", L"trim", L"-c", L".", L".a."}, 0, L"a\n"}, + {{L"string", L"trim", L"-c", L"\\/", L"/a\\"}, 0, L"a\n"}, + {{L"string", L"trim", L"-c", L"\\/", L"a/"}, 0, L"a\n"}, + {{L"string", L"trim", L"-c", L"\\/", L"\\a/"}, 0, L"a\n"}, + {{L"string", L"trim", L"-c", L"", L".a."}, 1, L".a.\n"}, - { {0}, 0, 0 } - }; + {{0}, 0, 0}}; struct string_test *t = string_tests; - while (t->argv[0] != 0) - { + while (t->argv[0] != 0) { run_one_string_test(t->argv, t->expected_rc, t->expected_out); t++; } } -/** - Main test -*/ -int main(int argc, char **argv) -{ +/// Main test. +int main(int argc, char **argv) { // Look for the file tests/test.fish. We expect to run in a directory containing that file. - // If we don't find it, walk up the directory hierarchy until we do, or error - while (access("./tests/test.fish", F_OK) != 0) - { + // If we don't find it, walk up the directory hierarchy until we do, or error. + while (access("./tests/test.fish", F_OK) != 0) { char wd[PATH_MAX + 1] = {}; - if (!getcwd(wd, sizeof wd)) - { + if (!getcwd(wd, sizeof wd)) { perror("getcwd"); exit(-1); } - if (! strcmp(wd, "/")) - { - fprintf(stderr, "Unable to find 'tests' directory, which should contain file test.fish\n"); + if (!strcmp(wd, "/")) { + fprintf(stderr, + "Unable to find 'tests' directory, which should contain file test.fish\n"); exit(EXIT_FAILURE); } - if (chdir(dirname(wd)) < 0) - { + if (chdir(dirname(wd)) < 0) { perror("chdir"); } } setlocale(LC_ALL, ""); - //srand(time(0)); + // srand(time(0)); configure_thread_assertions_for_testing(); - program_name=L"(ignore)"; + program_name = L"(ignore)"; s_arguments = argv + 1; say(L"Testing low-level functionality"); @@ -4440,12 +3896,13 @@ int main(int argc, char **argv) reader_init(); env_init(); - /* Set default signal handlers, so we can ctrl-C out of this */ + // Set default signal handlers, so we can ctrl-C out of this. signal_reset_handlers(); if (should_test_function("highlighting")) test_highlighting(); if (should_test_function("new_parser_ll2")) test_new_parser_ll2(); - if (should_test_function("new_parser_fuzzing")) test_new_parser_fuzzing(); //fuzzing is expensive + if (should_test_function("new_parser_fuzzing")) + test_new_parser_fuzzing(); // fuzzing is expensive if (should_test_function("new_parser_correctness")) test_new_parser_correctness(); if (should_test_function("new_parser_ad_hoc")) test_new_parser_ad_hoc(); if (should_test_function("new_parser_errors")) test_new_parser_errors(); @@ -4491,22 +3948,19 @@ int main(int argc, char **argv) // history_tests_t::test_history_speed(); say(L"Encountered %d errors in low-level tests", err_count); - if (s_test_run_count == 0) - say(L"*** No Tests Were Actually Run! ***"); + if (s_test_run_count == 0) say(L"*** No Tests Were Actually Run! ***"); - /* - Skip performance tests for now, since they seem to hang when running from inside make (?) - */ -// say( L"Testing performance" ); -// perf_complete(); + // Skip performance tests for now, since they seem to hang when running from inside make. + + // say( L"Testing performance" ); + // perf_complete(); reader_destroy(); builtin_destroy(); event_destroy(); proc_destroy(); - if (err_count != 0) - { - return(1); + if (err_count != 0) { + return (1); } } diff --git a/src/fish_version.cpp b/src/fish_version.cpp index c4eb15dfc..d371132f9 100644 --- a/src/fish_version.cpp +++ b/src/fish_version.cpp @@ -1,18 +1,12 @@ -/** \file fish_version.c Fish version receiver. - - This file has a specific purpose of shortening compilation times when - the only change is different `git describe` version. -*/ - +// Fish version receiver. +// +// This file has a specific purpose of shortening compilation times when +// the only change is different `git describe` version. #include "fish_version.h" #ifndef FISH_BUILD_VERSION #include "fish-build-version.h" #endif -/** - * Return fish shell version. - */ -const char *get_fish_version() { - return FISH_BUILD_VERSION; -} +/// Return fish shell version. +const char *get_fish_version() { return FISH_BUILD_VERSION; } diff --git a/src/fish_version.h b/src/fish_version.h index 61938c16d..419c83d56 100644 --- a/src/fish_version.h +++ b/src/fish_version.h @@ -1,5 +1,2 @@ -/** \file fish_version.h - Prototype for version receiver. -*/ - +// Prototype for version receiver. const char *get_fish_version(); From d3f155d895dbaeb92ef780c3aae355898b69bd27 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sat, 30 Apr 2016 20:31:02 -0700 Subject: [PATCH 186/363] restyle function module to match project style Reduces lint errors from 39 to 27 (-31%). Line count from 619 to 498 (-20%). Another step in resolving issue #2902. --- src/function.cpp | 360 +++++++++++++++++++---------------------------- src/function.h | 201 ++++++++++---------------- 2 files changed, 220 insertions(+), 341 deletions(-) diff --git a/src/function.cpp b/src/function.cpp index 4d731ff3b..7d0b797b1 100644 --- a/src/function.cpp +++ b/src/function.cpp @@ -1,96 +1,76 @@ -/** \file function.c - - Prototypes for functions for storing and retrieving function - information. These functions also take care of autoloading - functions in the $fish_function_path. Actual function evaluation - is taken care of by the parser and to some degree the builtin - handling library. -*/ +// Functions for storing and retrieving function information. These functions also take care of +// autoloading functions in the $fish_function_path. Actual function evaluation is taken care of by +// the parser and to some degree the builtin handling library. +// // IWYU pragma: no_include -#include -#include -#include -#include #include +#include #include +#include +#include +#include +#include #include #include -#include -#include "wutil.h" // IWYU pragma: keep -#include "fallback.h" // IWYU pragma: keep #include "autoload.h" -#include "function.h" #include "common.h" -#include "intern.h" -#include "event.h" -#include "reader.h" -#include "parser_keywords.h" #include "env.h" +#include "event.h" +#include "fallback.h" // IWYU pragma: keep +#include "function.h" +#include "intern.h" +#include "parser_keywords.h" +#include "reader.h" +#include "wutil.h" // IWYU pragma: keep -/** - Table containing all functions -*/ +/// Table containing all functions. typedef std::map function_map_t; static function_map_t loaded_functions; -/** - Functions that shouldn't be autoloaded (anymore). -*/ +/// Functions that shouldn't be autoloaded (anymore). static std::set function_tombstones; -/* Lock for functions */ +/// Lock for functions. static pthread_mutex_t functions_lock; -/* Autoloader for functions */ -class function_autoload_t : public autoload_t -{ -public: +/// Autoloader for functions. +class function_autoload_t : public autoload_t { + public: function_autoload_t(); virtual void command_removed(const wcstring &cmd); }; static function_autoload_t function_autoloader; -/** Constructor */ -function_autoload_t::function_autoload_t() : autoload_t(L"fish_function_path", NULL, 0) -{ -} +/// Constructor +function_autoload_t::function_autoload_t() : autoload_t(L"fish_function_path", NULL, 0) {} static bool function_remove_ignore_autoload(const wcstring &name, bool tombstone = true); -/** Callback when an autoloaded function is removed */ -void function_autoload_t::command_removed(const wcstring &cmd) -{ +/// Callback when an autoloaded function is removed. +void function_autoload_t::command_removed(const wcstring &cmd) { function_remove_ignore_autoload(cmd, false); } -/** - Kludgy flag set by the load function in order to tell function_add - that the function being defined is autoloaded. There should be a - better way to do this... -*/ +/// Kludgy flag set by the load function in order to tell function_add that the function being +/// defined is autoloaded. There should be a better way to do this... 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) -{ +/// Make sure that if the specified function is a dynamically loaded function, it has been fully +/// loaded. +static int load(const wcstring &name) { ASSERT_IS_MAIN_THREAD(); scoped_lock lock(functions_lock); bool was_autoload = is_autoload; int res; bool no_more_autoload = function_tombstones.count(name) > 0; - if (no_more_autoload) - return 0; + if (no_more_autoload) return 0; function_map_t::iterator iter = loaded_functions.find(name); - if (iter != loaded_functions.end() && !iter->second.is_autoload) - { - /* We have a non-autoload version already */ + if (iter != loaded_functions.end() && !iter->second.is_autoload) { + // We have a non-autoload version already. return 0; } @@ -100,41 +80,31 @@ static int load(const wcstring &name) return res; } -/** - Insert a list of all dynamically loaded functions into the - specified list. -*/ -static void autoload_names(std::set &names, int get_hidden) -{ +/// Insert a list of all dynamically loaded functions into the specified list. +static void autoload_names(std::set &names, int get_hidden) { size_t i; - const env_var_t path_var_wstr = env_get_string(L"fish_function_path"); - if (path_var_wstr.missing()) - return; + 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(); wcstring_list_t path_list; tokenize_variable_array(path_var, path_list); - for (i=0; i &names, int get_hidden) } } -void function_init() -{ - /* PCA: This recursive lock was introduced early in my work. I would like to make this a non-recursive lock but I haven't fully investigated all the call paths (for autoloading functions, etc.) */ +void function_init() { + // PCA: This recursive lock was introduced early in my work. I would like to make this a + // non-recursive lock but I haven't fully investigated all the call paths (for autoloading + // functions, etc.). pthread_mutexattr_t a; VOMIT_ON_FAILURE(pthread_mutexattr_init(&a)); - VOMIT_ON_FAILURE(pthread_mutexattr_settype(&a,PTHREAD_MUTEX_RECURSIVE)); + VOMIT_ON_FAILURE(pthread_mutexattr_settype(&a, PTHREAD_MUTEX_RECURSIVE)); VOMIT_ON_FAILURE(pthread_mutex_init(&functions_lock, &a)); VOMIT_ON_FAILURE(pthread_mutexattr_destroy(&a)); } -static std::map snapshot_vars(const wcstring_list_t &vars) -{ - std::map result; - for (wcstring_list_t::const_iterator it = vars.begin(), end = vars.end(); it != end; ++it) - { +static std::map snapshot_vars(const wcstring_list_t &vars) { + std::map result; + for (wcstring_list_t::const_iterator it = vars.begin(), end = vars.end(); it != end; ++it) { result.insert(std::make_pair(*it, env_get_string(*it))); } return result; } -function_info_t::function_info_t(const function_data_t &data, const wchar_t *filename, int def_offset, bool autoload) : - definition(data.definition), - description(data.description), - definition_file(intern(filename)), - definition_offset(def_offset), - named_arguments(data.named_arguments), - inherit_vars(snapshot_vars(data.inherit_vars)), - is_autoload(autoload), - shadows(data.shadows) -{ -} +function_info_t::function_info_t(const function_data_t &data, const wchar_t *filename, + int def_offset, bool autoload) + : definition(data.definition), + description(data.description), + definition_file(intern(filename)), + definition_offset(def_offset), + named_arguments(data.named_arguments), + inherit_vars(snapshot_vars(data.inherit_vars)), + is_autoload(autoload), + shadows(data.shadows) {} -function_info_t::function_info_t(const function_info_t &data, const wchar_t *filename, int def_offset, bool autoload) : - definition(data.definition), - description(data.description), - definition_file(intern(filename)), - definition_offset(def_offset), - named_arguments(data.named_arguments), - inherit_vars(data.inherit_vars), - is_autoload(autoload), - shadows(data.shadows) -{ -} +function_info_t::function_info_t(const function_info_t &data, const wchar_t *filename, + int def_offset, bool autoload) + : definition(data.definition), + description(data.description), + definition_file(intern(filename)), + definition_offset(def_offset), + named_arguments(data.named_arguments), + inherit_vars(data.inherit_vars), + is_autoload(autoload), + shadows(data.shadows) {} -void function_add(const function_data_t &data, const parser_t &parser, int definition_line_offset) -{ +void function_add(const function_data_t &data, const parser_t &parser, int definition_line_offset) { ASSERT_IS_MAIN_THREAD(); - CHECK(! data.name.empty(),); - CHECK(data.definition,); + CHECK(!data.name.empty(), ); + CHECK(data.definition, ); scoped_lock lock(functions_lock); - /* Remove the old function */ + // Remove the old function. function_remove(data.name); - /* Create and store a new function */ + // Create and store a new function. const wchar_t *filename = reader_current_filename(); - const function_map_t::value_type new_pair(data.name, function_info_t(data, filename, definition_line_offset, is_autoload)); + const function_map_t::value_type new_pair( + data.name, function_info_t(data, filename, definition_line_offset, is_autoload)); loaded_functions.insert(new_pair); - /* Add event handlers */ - for (std::vector::const_iterator iter = data.events.begin(); iter != data.events.end(); ++iter) - { + // Add event handlers. + for (std::vector::const_iterator iter = data.events.begin(); iter != data.events.end(); + ++iter) { event_add_handler(*iter); } } -int function_exists(const wcstring &cmd) -{ - if (parser_keywords_is_reserved(cmd)) - return 0; +int function_exists(const wcstring &cmd) { + if (parser_keywords_is_reserved(cmd)) return 0; scoped_lock lock(functions_lock); load(cmd); return loaded_functions.find(cmd) != loaded_functions.end(); } -void function_load(const wcstring &cmd) -{ - if (! parser_keywords_is_reserved(cmd)) - { +void function_load(const wcstring &cmd) { + if (!parser_keywords_is_reserved(cmd)) { scoped_lock lock(functions_lock); load(cmd); } } -int function_exists_no_autoload(const wcstring &cmd, const env_vars_snapshot_t &vars) -{ - if (parser_keywords_is_reserved(cmd)) - return 0; +int function_exists_no_autoload(const wcstring &cmd, const env_vars_snapshot_t &vars) { + if (parser_keywords_is_reserved(cmd)) return 0; scoped_lock lock(functions_lock); - return loaded_functions.find(cmd) != loaded_functions.end() || function_autoloader.can_load(cmd, vars); + return loaded_functions.find(cmd) != loaded_functions.end() || + function_autoloader.can_load(cmd, vars); } -static bool function_remove_ignore_autoload(const wcstring &name, bool tombstone) -{ - // Note: the lock may be held at this point, but is recursive +static bool function_remove_ignore_autoload(const wcstring &name, bool tombstone) { + // Note: the lock may be held at this point, but is recursive. scoped_lock lock(functions_lock); function_map_t::iterator iter = loaded_functions.find(name); - // not found. not erasing. - if (iter == loaded_functions.end()) - return false; + // Not found. Not erasing. + if (iter == loaded_functions.end()) return false; - // removing an auto-loaded function. prevent it from being - // auto-reloaded. - if (iter->second.is_autoload && tombstone) - function_tombstones.insert(name); + // Removing an auto-loaded function. Prevent it from being auto-reloaded. + if (iter->second.is_autoload && tombstone) function_tombstones.insert(name); loaded_functions.erase(iter); - event_t ev(EVENT_ANY); - ev.function_name=name; + ev.function_name = name; event_remove(ev); - return true; } -void function_remove(const wcstring &name) -{ - if (function_remove_ignore_autoload(name)) - function_autoloader.unload(name); +void function_remove(const wcstring &name) { + if (function_remove_ignore_autoload(name)) function_autoloader.unload(name); } -static const function_info_t *function_get(const wcstring &name) -{ - // The caller must lock the functions_lock before calling this; however our mutex is currently recursive, so trylock will never fail - // 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); +static const function_info_t *function_get(const wcstring &name) { + // The caller must lock the functions_lock before calling this; however our mutex is currently + // recursive, so trylock will never fail. 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; } } -bool function_get_definition(const wcstring &name, wcstring *out_definition) -{ +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; } -wcstring_list_t function_get_named_arguments(const wcstring &name) -{ +wcstring_list_t function_get_named_arguments(const wcstring &name) { scoped_lock lock(functions_lock); const function_info_t *func = function_get(name); return func ? func->named_arguments : wcstring_list_t(); } -std::map function_get_inherit_vars(const wcstring &name) -{ +std::map function_get_inherit_vars(const wcstring &name) { scoped_lock lock(functions_lock); const function_info_t *func = function_get(name); - return func ? func->inherit_vars : std::map(); + return func ? func->inherit_vars : std::map(); } -int function_get_shadows(const wcstring &name) -{ +int function_get_shadows(const wcstring &name) { scoped_lock lock(functions_lock); const function_info_t *func = function_get(name); return func ? func->shadows : false; } - -bool function_get_desc(const wcstring &name, wcstring *out_desc) -{ - /* Empty length string goes to NULL */ +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) -{ +void function_set_desc(const wcstring &name, const wcstring &desc) { 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; } } -bool function_copy(const wcstring &name, const wcstring &new_name) -{ +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. - const function_map_t::value_type new_pair(new_name, function_info_t(iter->second, NULL, 0, false)); + 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; } -wcstring_list_t function_get_names(int get_hidden) -{ +wcstring_list_t function_get_names(int get_hidden) { std::set names; scoped_lock lock(functions_lock); 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) - { + // Maybe skip hidden. + if (!get_hidden) { if (name.empty() || name.at(0) == L'_') continue; } names.insert(name); @@ -380,46 +316,40 @@ wcstring_list_t function_get_names(int get_hidden) return wcstring_list_t(names.begin(), names.end()); } -const wchar_t *function_get_definition_file(const wcstring &name) -{ +const wchar_t *function_get_definition_file(const wcstring &name) { scoped_lock lock(functions_lock); const function_info_t *func = function_get(name); return func ? func->definition_file : NULL; } - -int function_get_definition_offset(const wcstring &name) -{ +int function_get_definition_offset(const wcstring &name) { scoped_lock lock(functions_lock); const function_info_t *func = function_get(name); return func ? func->definition_offset : -1; } -void function_prepare_environment(const wcstring &name, const wchar_t * const * argv, const std::map &inherited_vars) -{ - /* Three components of the environment: - 1. argv - 2. named arguments - 3. inherited variables - */ +void function_prepare_environment(const wcstring &name, const wchar_t *const *argv, + const std::map &inherited_vars) { + // Three components of the environment: + // 1. argv + // 2. named arguments + // 3. inherited variables env_set_argv(argv); - + const wcstring_list_t named_arguments = function_get_named_arguments(name); - if (! named_arguments.empty()) - { - const wchar_t * const *arg; + if (!named_arguments.empty()) { + const wchar_t *const *arg; size_t i; - for (i=0, arg=argv; i < named_arguments.size(); i++) - { + for (i = 0, arg = argv; i < named_arguments.size(); i++) { env_set(named_arguments.at(i).c_str(), *arg, ENV_LOCAL | ENV_USER); - - if (*arg) - arg++; + + if (*arg) arg++; } } - - for (std::map::const_iterator it = inherited_vars.begin(), end = inherited_vars.end(); it != end; ++it) - { + + for (std::map::const_iterator it = inherited_vars.begin(), + end = inherited_vars.end(); + it != end; ++it) { env_set(it->first, it->second.missing() ? NULL : it->second.c_str(), ENV_LOCAL | ENV_USER); } } diff --git a/src/function.h b/src/function.h index 8ac45a11f..04829cbe9 100644 --- a/src/function.h +++ b/src/function.h @@ -1,194 +1,143 @@ -/** \file function.h - - Prototypes for functions for storing and retrieving function - information. These functions also take care of autoloading - functions in the $fish_function_path. Actual function evaluation - is taken care of by the parser and to some degree the builtin - handling library. -*/ +// Prototypes for functions for storing and retrieving function information. These functions also +// take care of autoloading functions in the $fish_function_path. Actual function evaluation is +// taken care of by the parser and to some degree the builtin handling library. #ifndef FISH_FUNCTION_H #define FISH_FUNCTION_H -#include -#include #include +#include +#include #include "common.h" -#include "event.h" #include "env.h" +#include "event.h" class parser_t; -/** - Structure describing a function. This is used by the parser to - store data on a function while parsing it. It is not used - internally to store functions, the function_internal_data_t - structure is used for that purpose. Parhaps these two should be - merged. - */ -struct function_data_t -{ - /** - Name of function - */ +/// Structure describing a function. This is used by the parser to store data on a function while +/// parsing it. It is not used internally to store functions, the function_internal_data_t structure +/// is used for that purpose. Parhaps these two should be merged. +struct function_data_t { + /// Name of function. wcstring name; - /** - Description of function - */ + /// Description of function. wcstring description; - /** - Function definition - */ + /// Function definition. const wchar_t *definition; - /** - List of all event handlers for this function - */ + /// List of all event handlers for this function. std::vector events; - /** - List of all named arguments for this function - */ + /// List of all named arguments for this function. wcstring_list_t named_arguments; - /** - List of all variables that are inherited from the function definition scope. - The variable values are snapshotted when function_add() is called. - */ + /// List of all variables that are inherited from the function definition scope. The variable + /// values are snapshotted when function_add() is called. wcstring_list_t inherit_vars; - /** - Set to non-zero if invoking this function shadows the variables - of the underlying function. - */ + /// Set to non-zero if invoking this function shadows the variables of the underlying function. int shadows; }; -class function_info_t -{ -public: - /** Constructs relevant information from the function_data */ - function_info_t(const function_data_t &data, const wchar_t *filename, int def_offset, bool autoload); +class function_info_t { + public: + /// Constructs relevant information from the function_data. + function_info_t(const function_data_t &data, const wchar_t *filename, int def_offset, + bool autoload); - /** Used by function_copy */ - function_info_t(const function_info_t &data, const wchar_t *filename, int def_offset, bool autoload); + /// Used by function_copy. + function_info_t(const function_info_t &data, const wchar_t *filename, int def_offset, + bool autoload); - /** Function definition */ + /// Function definition. const wcstring definition; - /** Function description. Only the description may be changed after the function is created. */ + /// Function description. Only the description may be changed after the function is created. wcstring description; - /** File where this function was defined (intern'd string) */ - const wchar_t * const definition_file; + /// File where this function was defined (intern'd string). + const wchar_t *const definition_file; - /** Line where definition started */ + /// 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; - /** Mapping of all variables that were inherited from the function definition scope to their values */ - const std::map inherit_vars; + /// Mapping of all variables that were inherited from the function definition scope to their + /// values. + const std::map inherit_vars; - /** 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. */ + /// Set to true if invoking this function shadows the variables of the underlying function. const bool shadows; }; - -/** - Initialize function data -*/ +/// Initialize function data. void function_init(); -/** Add a function. definition_line_offset is the line number of the function's definition within its source file */ -void function_add(const function_data_t &data, const parser_t &parser, int definition_line_offset = 0); +/// Add a function. definition_line_offset is the line number of the function's definition within +/// its source file. +void function_add(const function_data_t &data, const parser_t &parser, + int definition_line_offset = 0); -/** - Remove the function with the specified name. -*/ +/// Remove the function with the specified 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. -*/ +/// 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); -/** - 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. -*/ +/// 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); -/** - Sets the description of the function with the name \c name. -*/ +/// Sets the description of the function with the name \c name. void function_set_desc(const wcstring &name, const wcstring &desc); -/** - Returns true if the function with the name name exists. -*/ +/// Returns true if the function with the name name exists. int function_exists(const wcstring &name); -/** Attempts to load a function if not yet loaded. This is used by the completion machinery. */ +/// Attempts to load a function if not yet loaded. This is used by the completion machinery. void function_load(const wcstring &name); -/** - Returns true if the function with the name name exists, without triggering autoload. -*/ +/// 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); -/** - Returns all function names. - - \param get_hidden whether to include hidden functions, i.e. ones starting with an underscore -*/ +/// 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); -/** - Returns tha absolute path of the file where the specified function - was defined. Returns 0 if the file was defined on the commandline. - - This function does not autoload functions, it will only work on - functions that have already been defined. - - This returns an intern'd string. -*/ +/// Returns tha absolute path of the file where the specified function was defined. Returns 0 if the +/// file was defined on the commandline. +/// +/// This function does not autoload functions, it will only work on functions that have already been +/// defined. +/// +/// This returns an intern'd string. const wchar_t *function_get_definition_file(const wcstring &name); -/** - Returns the linenumber where the definition of the specified - function started. - - This function does not autoload functions, it will only work on - functions that have already been defined. -*/ +/// Returns the linenumber where the definition of the specified function started. +/// +/// 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); -/** - Returns a list of all named arguments of the specified function. -*/ +/// Returns a list of all named arguments of the specified function. wcstring_list_t function_get_named_arguments(const wcstring &name); -/** - Returns a mapping of all variables of the specified function that were inherited - from the scope of the function definition to their values. - */ -std::map function_get_inherit_vars(const wcstring &name); +/// Returns a mapping of all variables of the specified function that were inherited from the scope +/// of the function definition to their values. +std::map function_get_inherit_vars(const wcstring &name); -/** - Creates a new function using the same definition as the specified function. - Returns true if copy is successful. -*/ +/// 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); -/** - Returns whether this function shadows variables of the underlying function -*/ +/// Returns whether this function shadows variables of the underlying function. int function_get_shadows(const wcstring &name); -/** Prepares the environment for executing a function. -*/ -void function_prepare_environment(const wcstring &name, const wchar_t * const * argv, const std::map &inherited_vars); +/// Prepares the environment for executing a function. +void function_prepare_environment(const wcstring &name, const wchar_t *const *argv, + const std::map &inherited_vars); #endif From 7378871768fcc5ca9db6b82eacffdea290bb9805 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sat, 30 Apr 2016 20:47:05 -0700 Subject: [PATCH 187/363] restyle highlight module to match project style Reduces lint errors from 176 to 69 (-61%). Line count from 1627 to 1426 (-12%). Another step in resolving issue #2902. --- src/highlight.cpp | 1448 +++++++++++++++++++-------------------------- src/highlight.h | 165 +++--- 2 files changed, 707 insertions(+), 906 deletions(-) diff --git a/src/highlight.cpp b/src/highlight.cpp index 827a324c3..a92edae30 100644 --- a/src/highlight.cpp +++ b/src/highlight.cpp @@ -1,89 +1,70 @@ -/** \file highlight.c - Functions for syntax highlighting -*/ +// Functions for syntax highlighting. // IWYU pragma: no_include +#include +#include +#include #include #include -#include #include +#include #include -#include #include +#include #include #include -#include #include -#include -#include -#include "fallback.h" // IWYU pragma: keep -#include "wutil.h" // IWYU pragma: keep -#include "highlight.h" -#include "tokenizer.h" -#include "parse_util.h" -#include "parse_constants.h" #include "builtin.h" -#include "function.h" +#include "color.h" +#include "common.h" #include "env.h" #include "expand.h" -#include "common.h" -#include "output.h" -#include "wildcard.h" -#include "path.h" +#include "fallback.h" // IWYU pragma: keep +#include "function.h" +#include "highlight.h" #include "history.h" +#include "output.h" +#include "parse_constants.h" #include "parse_tree.h" -#include "color.h" +#include "parse_util.h" +#include "path.h" +#include "tokenizer.h" +#include "wildcard.h" +#include "wutil.h" // IWYU pragma: keep #define CURSOR_POSITION_INVALID ((size_t)(-1)) -/** - Number of elements in the highlight_var array -*/ -#define VAR_COUNT ( sizeof(highlight_var)/sizeof(wchar_t *) ) +/// Number of elements in the highlight_var array. +#define VAR_COUNT (sizeof(highlight_var) / sizeof(wchar_t *)) -/** The environment variables used to specify the color of different tokens. This matches the order in highlight_spec_t */ -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_autosuggestion", - L"fish_color_selection", +/// The environment variables used to specify the color of different tokens. This matches the order +/// in highlight_spec_t. +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_autosuggestion", L"fish_color_selection", - 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" }; -/* Determine if the filesystem containing the given fd is case insensitive. */ +/// Determine if the filesystem containing the given fd is case insensitive. typedef std::map case_sensitivity_cache_t; -bool fs_is_case_insensitive(const wcstring &path, int fd, case_sensitivity_cache_t &case_sensitivity_cache) -{ - /* If _PC_CASE_SENSITIVE is not defined, assume case sensitive */ +bool fs_is_case_insensitive(const wcstring &path, int fd, + case_sensitivity_cache_t &case_sensitivity_cache) { + // If _PC_CASE_SENSITIVE is not defined, assume case sensitive. bool result = false; #ifdef _PC_CASE_SENSITIVE - /* Try the cache first */ + // 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 - { - /* 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 */ + } 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); case_sensitivity_cache[path] = result; @@ -92,14 +73,16 @@ bool fs_is_case_insensitive(const wcstring &path, int fd, case_sensitivity_cache return result; } -/* Tests whether the specified string cpath is the prefix of anything we could cd to. directories is a list of possible parent directories (typically either the working directory, or the cdpath). This does I/O! - - Hack: if out_suggested_cdpath is not NULL, it returns the autosuggestion for cd. This descends the deepest unique directory hierarchy. - - We expect the path to already be unescaped. -*/ -bool is_potential_path(const wcstring &potential_path_fragment, const wcstring_list_t &directories, path_flags_t flags) -{ +/// Tests whether the specified string cpath is the prefix of anything we could cd to. directories +/// is a list of possible parent directories (typically either the working directory, or the +/// cdpath). This does I/O! +/// +/// Hack: if out_suggested_cdpath is not NULL, it returns the autosuggestion for cd. This descends +/// the deepest unique directory hierarchy. +/// +/// We expect the path to already be unescaped. +bool is_potential_path(const wcstring &potential_path_fragment, const wcstring_list_t &directories, + path_flags_t flags) { ASSERT_IS_BACKGROUND_THREAD(); const bool require_dir = !!(flags & PATH_REQUIRE_DIR); @@ -108,16 +91,13 @@ bool is_potential_path(const wcstring &potential_path_fragment, const wcstring_l bool result = false; wcstring path_with_magic(potential_path_fragment); - if (flags & PATH_EXPAND_TILDE) - expand_tilde(path_with_magic); + if (flags & PATH_EXPAND_TILDE) expand_tilde(path_with_magic); - // debug( 1, L"%ls -> %ls ->%ls", path, tilde, unescaped ); + // debug( 1, L"%ls -> %ls ->%ls", path, tilde, unescaped ); - for (size_t i=0; i < path_with_magic.size(); i++) - { + for (size_t i = 0; i < path_with_magic.size(); i++) { wchar_t c = path_with_magic.at(i); - switch (c) - { + switch (c) { case PROCESS_EXPAND: case VARIABLE_EXPAND: case VARIABLE_EXPAND_SINGLE: @@ -126,97 +106,83 @@ bool is_potential_path(const wcstring &potential_path_fragment, const wcstring_l case BRACKET_SEP: case ANY_CHAR: case ANY_STRING: - case ANY_STRING_RECURSIVE: - { + case ANY_STRING_RECURSIVE: { has_magic = 1; break; } - - case INTERNAL_SEPARATOR: - { + case INTERNAL_SEPARATOR: { break; } - - default: - { + default: { clean_potential_path_fragment.push_back(c); break; } - } } - if (! has_magic && ! clean_potential_path_fragment.empty()) - { - /* Don't test the same path multiple times, which can happen if the path is absolute and the CDPATH contains multiple entries */ + if (!has_magic && !clean_potential_path_fragment.empty()) { + // Don't test the same path multiple times, which can happen if the path is absolute and the + // CDPATH contains multiple entries. std::set checked_paths; - /* Keep a cache of which paths / filesystems are case sensitive */ + // 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 = path_apply_working_directory(clean_potential_path_fragment, wd); + const wcstring abs_path = + path_apply_working_directory(clean_potential_path_fragment, wd); - /* Skip this if it's empty or we've already checked it */ - if (abs_path.empty() || checked_paths.count(abs_path)) - continue; + // Skip this if it's empty or we've already checked it. + if (abs_path.empty() || checked_paths.count(abs_path)) continue; checked_paths.insert(abs_path); - /* If we end with a slash, then it must be a directory */ - bool must_be_full_dir = abs_path.at(abs_path.size()-1) == L'/'; - if (must_be_full_dir) - { + // If we end with a slash, then it must be a directory. + bool must_be_full_dir = abs_path.at(abs_path.size() - 1) == L'/'; + if (must_be_full_dir) { 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; } - } - else - { - /* We do not end with a slash; it does not have to be a directory */ + } else { + // We do not end with a slash; it does not have to be a directory. DIR *dir = NULL; const wcstring dir_name = wdirname(abs_path); const wcstring filename_fragment = wbasename(abs_path); - if (dir_name == L"/" && filename_fragment == L"/") - { - /* cd ///.... No autosuggestion. */ + if (dir_name == L"/" && filename_fragment == L"/") { + // cd ///.... No autosuggestion. result = true; - } - else if ((dir = wopendir(dir_name))) - { - // Check if we're case insensitive - const bool do_case_insensitive = fs_is_case_insensitive(dir_name, dirfd(dir), case_sensitivity_cache); - + } else if ((dir = wopendir(dir_name))) { + // Check if we're case insensitive. + const bool do_case_insensitive = + fs_is_case_insensitive(dir_name, dirfd(dir), case_sensitivity_cache); + wcstring matched_file; - + // We opened the dir_name; look for a string where the base name prefixes it - // Don't ask for the is_dir value unless we care, because it can cause extra filesystem access + // Don't ask for the is_dir value unless we care, because it can cause extra + // filesystem access. wcstring ent; bool is_dir = false; - while (wreaddir_resolving(dir, dir_name, ent, require_dir ? &is_dir : NULL)) - { - // Maybe skip directories - if (require_dir && ! is_dir) - { + while (wreaddir_resolving(dir, dir_name, ent, require_dir ? &is_dir : NULL)) { + // Maybe skip directories. + if (require_dir && !is_dir) { continue; } - + if (string_prefixes_string(filename_fragment, ent) || - (do_case_insensitive && string_prefixes_string_case_insensitive(filename_fragment, ent))) - { - // We matched + (do_case_insensitive && + string_prefixes_string_case_insensitive(filename_fragment, ent))) { + // We matched. matched_file = ent; break; } } closedir(dir); - /* We succeeded if we found a match */ - result = ! matched_file.empty(); + // We succeeded if we found a match. + result = !matched_file.empty(); } } } @@ -224,53 +190,47 @@ bool is_potential_path(const wcstring &potential_path_fragment, const wcstring_l return result; } - -/* Given a string, return whether it prefixes a path that we could cd into. Return that path in out_path. Expects path to be unescaped. */ -static bool is_potential_cd_path(const wcstring &path, const wcstring &working_directory, path_flags_t flags) -{ +// Given a string, return whether it prefixes a path that we could cd into. Return that path in +// out_path. Expects path to be unescaped. +static bool is_potential_cd_path(const wcstring &path, const wcstring &working_directory, + path_flags_t flags) { wcstring_list_t directories; - if (string_prefixes_string(L"./", path)) - { - /* Ignore the CDPATH in this case; just use the working directory */ + if (string_prefixes_string(L"./", path)) { + // Ignore the CDPATH in this case; just use the working directory. directories.push_back(working_directory); - } - else - { - /* Get the CDPATH */ + } else { + // Get the CDPATH. env_var_t cdpath = env_get_string(L"CDPATH"); - if (cdpath.missing_or_empty()) - cdpath = L"."; + if (cdpath.missing_or_empty()) cdpath = L"."; - /* Tokenize it into directories */ + // Tokenize it into directories. wcstokenizer tokenizer(cdpath, ARRAY_SEP_STR); wcstring next_path; - while (tokenizer.next(next_path)) - { - /* Ensure that we use the working directory for relative cdpaths like "." */ + while (tokenizer.next(next_path)) { + // Ensure that we use the working directory for relative cdpaths like ".". directories.push_back(path_apply_working_directory(next_path, working_directory)); } } - /* Call is_potential_path with all of these directories */ + // Call is_potential_path with all of these directories. bool result = is_potential_path(path, directories, flags | PATH_REQUIRE_DIR); return result; } -/* Given a plain statement node in a parse tree, get the command and return it, expanded appropriately for commands. If we succeed, return true. */ -bool plain_statement_get_expanded_command(const wcstring &src, const parse_node_tree_t &tree, const parse_node_t &plain_statement, wcstring *out_cmd) -{ +// Given a plain statement node in a parse tree, get the command and return it, expanded +// appropriately for commands. If we succeed, return true. +bool plain_statement_get_expanded_command(const wcstring &src, const parse_node_tree_t &tree, + const parse_node_t &plain_statement, wcstring *out_cmd) { assert(plain_statement.type == symbol_plain_statement); bool result = false; - /* Get the command */ + // Get the command. wcstring cmd; - if (tree.command_for_plain_statement(plain_statement, src, &cmd)) - { - /* Try expanding it. If we cannot, it's an error. */ - if (expand_one(cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES | EXPAND_SKIP_JOBS)) - { - /* Success, return the expanded string by reference */ + if (tree.command_for_plain_statement(plain_statement, src, &cmd)) { + // Try expanding it. If we cannot, it's an error. + if (expand_one(cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES | EXPAND_SKIP_JOBS)) { + // Success, return the expanded string by reference. out_cmd->swap(cmd); result = true; } @@ -278,66 +238,53 @@ bool plain_statement_get_expanded_command(const wcstring &src, const parse_node_ return result; } - -rgb_color_t highlight_get_color(highlight_spec_t highlight, bool is_background) -{ +rgb_color_t highlight_get_color(highlight_spec_t highlight, bool is_background) { rgb_color_t result = rgb_color_t::normal(); - /* If sloppy_background is set, then we look at the foreground color even if is_background is set */ + // If sloppy_background is set, then we look at the foreground color even if is_background is + // set. bool treat_as_background = is_background && !(highlight & highlight_modifier_sloppy_background); - /* Get the primary variable */ + // Get the primary variable. size_t idx = highlight_get_primary(highlight); - if (idx >= VAR_COUNT) - { + if (idx >= VAR_COUNT) { return rgb_color_t::normal(); } env_var_t val_wstr = env_get_string(highlight_var[idx]); -// debug( 1, L"%d -> %d -> %ls", highlight, idx, val ); + // debug( 1, L"%d -> %d -> %ls", highlight, idx, val ); - if (val_wstr.missing()) - val_wstr = env_get_string(highlight_var[0]); + if (val_wstr.missing()) val_wstr = env_get_string(highlight_var[0]); - if (! val_wstr.missing()) - result = parse_color(val_wstr, treat_as_background); + if (!val_wstr.missing()) result = parse_color(val_wstr, treat_as_background); - /* Handle modifiers. */ - if (highlight & highlight_modifier_valid_path) - { - env_var_t val2_wstr = env_get_string(L"fish_color_valid_path"); + // Handle modifiers. + if (highlight & highlight_modifier_valid_path) { + env_var_t val2_wstr = env_get_string(L"fish_color_valid_path"); const wcstring val2 = val2_wstr.missing() ? L"" : val2_wstr.c_str(); rgb_color_t result2 = parse_color(val2, is_background); if (result.is_normal()) result = result2; - else - { - if (result2.is_bold()) - result.set_bold(true); - if (result2.is_underline()) - result.set_underline(true); + else { + if (result2.is_bold()) result.set_bold(true); + if (result2.is_underline()) result.set_underline(true); } } - if (highlight & highlight_modifier_force_underline) - { + if (highlight & highlight_modifier_force_underline) { result.set_underline(true); } return result; } - -static bool has_expand_reserved(const wcstring &str) -{ +static bool has_expand_reserved(const wcstring &str) { bool result = false; - for (size_t i=0; i < str.size(); i++) - { + for (size_t i = 0; i < str.size(); i++) { wchar_t wc = str.at(i); - if (wc >= EXPAND_RESERVED_BASE && wc <= EXPAND_RESERVED_END) - { + if (wc >= EXPAND_RESERVED_BASE && wc <= EXPAND_RESERVED_END) { result = true; break; } @@ -345,28 +292,31 @@ static bool has_expand_reserved(const wcstring &str) return result; } -/* Parse a command line. Return by reference the last command, and the last argument to that command (as a copied node), if any. This is used by autosuggestions */ -static bool autosuggest_parse_command(const wcstring &buff, wcstring *out_expanded_command, parse_node_t *out_last_arg) -{ +// Parse a command line. Return by reference the last command, and the last argument to that command +// (as a copied node), if any. This is used by autosuggestions. +static bool autosuggest_parse_command(const wcstring &buff, wcstring *out_expanded_command, + parse_node_t *out_last_arg) { bool result = false; - /* Parse the buffer */ + // Parse the buffer. parse_node_tree_t parse_tree; - parse_tree_from_string(buff, parse_flag_continue_after_error | parse_flag_accept_incomplete_tokens, &parse_tree, NULL); + parse_tree_from_string(buff, + parse_flag_continue_after_error | parse_flag_accept_incomplete_tokens, + &parse_tree, NULL); - /* Find the last statement */ - const parse_node_t *last_statement = parse_tree.find_last_node_of_type(symbol_plain_statement, NULL); - if (last_statement != NULL) - { - if (plain_statement_get_expanded_command(buff, parse_tree, *last_statement, out_expanded_command)) - { - /* We got it */ + // Find the last statement. + const parse_node_t *last_statement = + parse_tree.find_last_node_of_type(symbol_plain_statement, NULL); + if (last_statement != NULL) { + if (plain_statement_get_expanded_command(buff, parse_tree, *last_statement, + out_expanded_command)) { + // We got it. result = true; - /* Find the last argument. If we don't get one, return an invalid node. */ - const parse_node_t *last_arg = parse_tree.find_last_node_of_type(symbol_argument, last_statement); - if (last_arg != NULL) - { + // Find the last argument. If we don't get one, return an invalid node. + const parse_node_t *last_arg = + parse_tree.find_last_node_of_type(symbol_argument, last_statement); + if (last_arg != NULL) { *out_last_arg = *last_arg; } } @@ -374,74 +324,60 @@ static bool autosuggest_parse_command(const wcstring &buff, wcstring *out_expand 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; - /* Parse the string */ + // Parse the string. wcstring parsed_command; parse_node_t last_arg_node(token_type_invalid); - if (! autosuggest_parse_command(item.str(), &parsed_command, &last_arg_node)) - return false; + if (!autosuggest_parse_command(item.str(), &parsed_command, &last_arg_node)) return false; - if (parsed_command == L"cd" && last_arg_node.type == symbol_argument && last_arg_node.has_source()) - { - /* We can possibly handle this specially */ + if (parsed_command == L"cd" && last_arg_node.type == symbol_argument && + last_arg_node.has_source()) { + // We can possibly handle this specially. wcstring dir = last_arg_node.get_source(item.str()); - if (expand_one(dir, EXPAND_SKIP_CMDSUBST)) - { + 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) - { + bool is_help = + string_prefixes_string(dir, L"--help") || string_prefixes_string(dir, L"-h"); + 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)) - { - /* Don't suggest the working directory as the 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; } } } } - /* If not handled specially, handle it here */ - if (! handled) - { + // If not handled specially, handle it here. + if (!handled) { bool cmd_ok = false; - if (path_get_path(parsed_command, NULL)) - { + if (path_get_path(parsed_command, NULL)) { cmd_ok = true; - } - else if (builtin_exists(parsed_command) || function_exists_no_autoload(parsed_command, vars)) - { + } else if (builtin_exists(parsed_command) || + function_exists_no_autoload(parsed_command, vars)) { cmd_ok = true; } - if (cmd_ok) - { + if (cmd_ok) { const path_list_t &paths = item.get_required_paths(); - if (paths.empty()) - { - suggestionOK= true; - } - else - { + if (paths.empty()) { + suggestionOK = true; + } else { suggestionOK = detector.paths_are_valid(paths); } } @@ -450,63 +386,53 @@ bool autosuggest_validate_from_history(const history_item_t &item, file_detectio return suggestionOK; } -/* Highlights the variable starting with 'in', setting colors within the 'colors' array. Returns the number of characters consumed. */ -static size_t color_variable(const wchar_t *in, size_t in_len, std::vector::iterator colors) -{ +// Highlights the variable starting with 'in', setting colors within the 'colors' array. Returns the +// number of characters consumed. +static size_t color_variable(const wchar_t *in, size_t in_len, + std::vector::iterator colors) { assert(in_len > 0); assert(in[0] == L'$'); // Handle an initial run of $s. size_t idx = 0; size_t dollar_count = 0; - while (in[idx] == '$') - { - // Our color depends on the next char + while (in[idx] == '$') { + // Our color depends on the next char. wchar_t next = in[idx + 1]; - if (next == L'$' || wcsvarchr(next)) - { + if (next == L'$' || wcsvarchr(next)) { colors[idx] = highlight_spec_operator; - } - else - { + } else { colors[idx] = highlight_spec_error; } idx++; dollar_count++; } - // Handle a sequence of variable characters - while (wcsvarchr(in[idx])) - { + // Handle a sequence of variable characters. + while (wcsvarchr(in[idx])) { colors[idx++] = highlight_spec_operator; } - // Handle a slice, up to dollar_count of them. Note that we currently don't do any validation of the slice's contents, e.g. $foo[blah] will not show an error even though it's invalid. - for (size_t slice_count=0; slice_count < dollar_count && in[idx] == L'['; slice_count++) - { + // Handle a slice, up to dollar_count of them. Note that we currently don't do any validation of + // the slice's contents, e.g. $foo[blah] will not show an error even though it's invalid. + for (size_t slice_count = 0; slice_count < dollar_count && in[idx] == L'['; slice_count++) { wchar_t *slice_begin = NULL, *slice_end = NULL; int located = parse_util_locate_slice(in + idx, &slice_begin, &slice_end, false); - if (located == 1) - { + if (located == 1) { size_t slice_begin_idx = slice_begin - in, slice_end_idx = slice_end - in; assert(slice_end_idx > slice_begin_idx); colors[slice_begin_idx] = highlight_spec_operator; colors[slice_end_idx] = highlight_spec_operator; idx = slice_end_idx + 1; - } - else if (located == 0) - { + } else if (located == 0) { // not a slice break; - } - else - { + } else { assert(located < 0); - // syntax error - // Normally the entire token is colored red for us, but inside a double-quoted string - // that doesn't happen. As such, color the variable + the slice start red. Coloring any - // more than that looks bad, unless we're willing to try and detect where the double-quoted - // string ends, and I'd rather not do that. + // Syntax error. Normally the entire token is colored red for us, but inside a + // double-quoted string that doesn't happen. As such, color the variable + the slice + // start red. Coloring any more than that looks bad, unless we're willing to try and + // detect where the double-quoted string ends, and I'd rather not do that. std::fill(colors, colors + idx + 1, (highlight_spec_t)highlight_spec_error); break; } @@ -514,276 +440,205 @@ static size_t color_variable(const wchar_t *in, size_t in_len, std::vector::iterator colors) -{ +/// This function is a disaster badly in need of refactoring. It colors an argument, without regard +/// to command substitutions. +static void color_argument_internal(const wcstring &buffstr, + std::vector::iterator colors) { const size_t buff_len = buffstr.size(); std::fill(colors, colors + buff_len, (highlight_spec_t)highlight_spec_param); - enum {e_unquoted, e_single_quoted, e_double_quoted} mode = e_unquoted; - int bracket_count=0; - for (size_t in_pos=0; in_pos < buff_len; in_pos++) - { + enum { e_unquoted, e_single_quoted, e_double_quoted } mode = e_unquoted; + int bracket_count = 0; + for (size_t in_pos = 0; in_pos < buff_len; in_pos++) { const wchar_t c = buffstr.at(in_pos); - switch (mode) - { - case e_unquoted: - { - if (c == L'\\') - { - int fill_color = highlight_spec_escape; //may be set to highlight_error + switch (mode) { + case e_unquoted: { + if (c == L'\\') { + int fill_color = highlight_spec_escape; // may be set to highlight_error const size_t backslash_pos = in_pos; size_t fill_end = backslash_pos; - // Move to the escaped character + // Move to the escaped character. in_pos++; const wchar_t escaped_char = (in_pos < buff_len ? buffstr.at(in_pos) : L'\0'); - if (escaped_char == L'\0') - { + if (escaped_char == L'\0') { fill_end = in_pos; fill_color = highlight_spec_error; - } - else if (wcschr(L"~%", escaped_char)) - { - if (in_pos == 1) - { + } else if (wcschr(L"~%", escaped_char)) { + if (in_pos == 1) { fill_end = in_pos + 1; } - } - else if (escaped_char == L',') - { - if (bracket_count) - { + } else if (escaped_char == L',') { + if (bracket_count) { fill_end = in_pos + 1; } - } - else if (wcschr(L"abefnrtv*?$(){}[]'\"<>^ \\#;|&", escaped_char)) - { + } else if (wcschr(L"abefnrtv*?$(){}[]'\"<>^ \\#;|&", escaped_char)) { fill_end = in_pos + 1; - } - else if (wcschr(L"c", escaped_char)) - { - // Like \ci. So highlight three characters + } else if (wcschr(L"c", escaped_char)) { + // Like \ci. So highlight three characters. fill_end = in_pos + 1; - } - else if (wcschr(L"uUxX01234567", escaped_char)) - { - long long res=0; - int chars=2; - int base=16; - + } else if (wcschr(L"uUxX01234567", escaped_char)) { + long long res = 0; + int chars = 2; + int base = 16; wchar_t max_val = ASCII_MAX; - switch (escaped_char) - { - case L'u': - { - chars=4; + switch (escaped_char) { + case L'u': { + chars = 4; max_val = UCS2_MAX; in_pos++; break; } - - case L'U': - { - chars=8; + case L'U': { + chars = 8; max_val = WCHAR_MAX; in_pos++; break; } - - case L'x': - { + case L'x': { in_pos++; break; } - - case L'X': - { + case L'X': { max_val = BYTE_MAX; in_pos++; break; } - - default: - { + default: { // a digit like \12 - base=8; - chars=3; + base = 8; + chars = 3; break; } } // Consume - for (int i=0; i < chars && in_pos < buff_len; i++) - { + for (int i = 0; i < chars && in_pos < buff_len; i++) { long d = convert_digit(buffstr.at(in_pos), base); - if (d < 0) - break; + if (d < 0) break; res = (res * base) + d; in_pos++; } - //in_pos is now at the first character that could not be converted (or buff_len) + // in_pos is now at the first character that could not be converted (or + // buff_len). assert(in_pos >= backslash_pos && in_pos <= buff_len); fill_end = in_pos; - // It's an error if we exceeded the max value - if (res > max_val) - fill_color = highlight_spec_error; + // It's an error if we exceeded the max value. + if (res > max_val) fill_color = highlight_spec_error; - // Subtract one from in_pos, so that the increment in the loop will move to the next character + // Subtract one from in_pos, so that the increment in the loop will move to + // the next character. in_pos--; } assert(fill_end >= backslash_pos); std::fill(colors + backslash_pos, colors + fill_end, fill_color); - } - else - { - // Not a backslash - switch (c) - { + } else { + // Not a backslash. + switch (c) { case L'~': - case L'%': - { - if (in_pos == 0) - { + case L'%': { + if (in_pos == 0) { colors[in_pos] = highlight_spec_operator; } break; } - - case L'$': - { + case L'$': { assert(in_pos < buff_len); - in_pos += color_variable(buffstr.c_str() + in_pos, buff_len - in_pos, colors + in_pos); - /* subtract one to account for the upcoming loop increment */ + in_pos += color_variable(buffstr.c_str() + in_pos, buff_len - in_pos, + colors + in_pos); + // Subtract one to account for the upcoming loop increment. in_pos -= 1; break; } - - case L'*': case L'?': case L'(': - case L')': - { + case L')': { colors[in_pos] = highlight_spec_operator; break; } - - case L'{': - { + case L'{': { colors[in_pos] = highlight_spec_operator; bracket_count++; break; } - - case L'}': - { + case L'}': { colors[in_pos] = highlight_spec_operator; bracket_count--; break; } - - case L',': - { - if (bracket_count > 0) - { + case L',': { + if (bracket_count > 0) { colors[in_pos] = highlight_spec_operator; } - break; } - - case L'\'': - { + case L'\'': { colors[in_pos] = highlight_spec_quote; mode = e_single_quoted; break; } - - case L'\"': - { + case L'\"': { colors[in_pos] = highlight_spec_quote; mode = e_double_quoted; break; } - } } break; } - - /* - Mode 1 means single quoted string, i.e 'foo' - */ - case e_single_quoted: - { + // Mode 1 means single quoted string, i.e 'foo'. + case e_single_quoted: { colors[in_pos] = highlight_spec_quote; - if (c == L'\\') - { + if (c == L'\\') { // backslash - if (in_pos + 1 < buff_len) - { + if (in_pos + 1 < buff_len) { const wchar_t escaped_char = buffstr.at(in_pos + 1); - if (escaped_char == L'\\' || escaped_char == L'\'') - { - colors[in_pos] = highlight_spec_escape; //backslash - colors[in_pos + 1] = highlight_spec_escape; //escaped char - in_pos += 1; //skip over backslash + if (escaped_char == L'\\' || escaped_char == L'\'') { + colors[in_pos] = highlight_spec_escape; // backslash + colors[in_pos + 1] = highlight_spec_escape; // escaped char + in_pos += 1; // skip over backslash } } - } - else if (c == L'\'') - { + } else if (c == L'\'') { mode = e_unquoted; } break; } - - /* - Mode 2 means double quoted string, i.e. "foo" - */ - case e_double_quoted: - { - // slices are colored in advance, past `in_pos`, and we don't want to overwrite that - if (colors[in_pos] == highlight_spec_param) - { + // Mode 2 means double quoted string, i.e. "foo". + case e_double_quoted: { + // Slices are colored in advance, past `in_pos`, and we don't want to overwrite + // that. + if (colors[in_pos] == highlight_spec_param) { colors[in_pos] = highlight_spec_quote; } - switch (c) - { - case L'"': - { + switch (c) { + case L'"': { mode = e_unquoted; break; } - - case L'\\': - { - // backslash - if (in_pos + 1 < buff_len) - { + case L'\\': { + // Backslash + if (in_pos + 1 < buff_len) { const wchar_t escaped_char = buffstr.at(in_pos + 1); - if (wcschr(L"\\\"\n$", escaped_char)) - { - colors[in_pos] = highlight_spec_escape; //backslash - colors[in_pos + 1] = highlight_spec_escape; //escaped char - in_pos += 1; //skip over backslash + if (wcschr(L"\\\"\n$", escaped_char)) { + colors[in_pos] = highlight_spec_escape; // backslash + colors[in_pos + 1] = highlight_spec_escape; // escaped char + in_pos += 1; // skip over backslash } } break; } - - case L'$': - { - in_pos += color_variable(buffstr.c_str() + in_pos, buff_len - in_pos, colors + in_pos); - /* subtract one to account for the upcoming increment in the loop */ + case L'$': { + in_pos += color_variable(buffstr.c_str() + in_pos, buff_len - in_pos, + colors + in_pos); + // Subtract one to account for the upcoming increment in the loop. in_pos -= 1; break; } - } break; } @@ -791,142 +646,141 @@ static void color_argument_internal(const wcstring &buffstr, std::vector color_array_t; color_array_t color_array; - - /* The parse tree of the buff */ + // The parse tree of the buff. parse_node_tree_t parse_tree; - - /* Color an argument */ + // Color an argument. void color_argument(const parse_node_t &node); - - /* Color a redirection */ + // Color a redirection. void color_redirection(const parse_node_t &node); - - /* Color the arguments of the given node */ + // Color the arguments of the given node. void color_arguments(const parse_node_t &list_node); - - /* Color the redirections of the given node */ + // Color the redirections of the given node. void color_redirections(const parse_node_t &list_node); - - /* Color all the children of the command with the given type */ - void color_children(const parse_node_t &parent, parse_token_type_t type, highlight_spec_t color); - - /* Colors the source range of a node with a given color */ + // Color all the children of the command with the given type. + void color_children(const parse_node_t &parent, parse_token_type_t type, + highlight_spec_t color); + // Colors the source range of a node with a given color. void color_node(const parse_node_t &node, highlight_spec_t color); -public: - - /* Constructor */ - highlighter_t(const wcstring &str, size_t pos, const env_vars_snapshot_t &ev, const wcstring &wd, bool can_do_io) : buff(str), cursor_pos(pos), vars(ev), io_ok(can_do_io), working_directory(wd), color_array(str.size()) - { - /* Parse the tree */ - parse_tree_from_string(buff, parse_flag_continue_after_error | parse_flag_include_comments, &this->parse_tree, NULL); + public: + // Constructor + highlighter_t(const wcstring &str, size_t pos, const env_vars_snapshot_t &ev, + const wcstring &wd, bool can_do_io) + : buff(str), + cursor_pos(pos), + vars(ev), + io_ok(can_do_io), + working_directory(wd), + color_array(str.size()) { + // Parse the tree. + parse_tree_from_string(buff, parse_flag_continue_after_error | parse_flag_include_comments, + &this->parse_tree, NULL); } - /* Perform highlighting, returning an array of colors */ + // Perform highlighting, returning an array of colors. const color_array_t &highlight(); }; -void highlighter_t::color_node(const parse_node_t &node, highlight_spec_t color) -{ - // Can only color nodes with valid source ranges - if (! node.has_source() || node.source_length == 0) - return; +void highlighter_t::color_node(const parse_node_t &node, highlight_spec_t color) { + // Can only color nodes with valid source ranges. + if (!node.has_source() || node.source_length == 0) return; - // Fill the color array with our color in the corresponding range + // Fill the color array with our color in the corresponding range. size_t source_end = node.source_start + node.source_length; assert(source_end >= node.source_start); assert(source_end <= color_array.size()); - std::fill(this->color_array.begin() + node.source_start, this->color_array.begin() + source_end, color); + std::fill(this->color_array.begin() + node.source_start, this->color_array.begin() + source_end, + color); } -/* node does not necessarily have type symbol_argument here */ -void highlighter_t::color_argument(const parse_node_t &node) -{ - if (! node.has_source()) - return; +// node does not necessarily have type symbol_argument here. +void highlighter_t::color_argument(const parse_node_t &node) { + if (!node.has_source()) return; const wcstring arg_str = node.get_source(this->buff); - /* Get an iterator to the colors associated with the argument */ + // Get an iterator to the colors associated with the argument. const size_t arg_start = node.source_start; const color_array_t::iterator arg_colors = color_array.begin() + arg_start; - /* Color this argument without concern for command substitutions */ + // Color this argument without concern for command substitutions. color_argument_internal(arg_str, arg_colors); - /* Now do command substitutions */ + // Now do command substitutions. size_t cmdsub_cursor = 0, cmdsub_start = 0, cmdsub_end = 0; wcstring cmdsub_contents; - while (parse_util_locate_cmdsubst_range(arg_str, &cmdsub_cursor, &cmdsub_contents, &cmdsub_start, &cmdsub_end, true /* accept incomplete */) > 0) - { - /* The cmdsub_start is the open paren. cmdsub_end is either the close paren or the end of the string. cmdsub_contents extends from one past cmdsub_start to cmdsub_end */ + while (parse_util_locate_cmdsubst_range(arg_str, &cmdsub_cursor, &cmdsub_contents, + &cmdsub_start, &cmdsub_end, + true /* accept incomplete */) > 0) { + // The cmdsub_start is the open paren. cmdsub_end is either the close paren or the end of + // the string. cmdsub_contents extends from one past cmdsub_start to cmdsub_end. assert(cmdsub_end > cmdsub_start); assert(cmdsub_end - cmdsub_start - 1 == cmdsub_contents.size()); - /* Found a command substitution. Compute the position of the start and end of the cmdsub contents, within our overall src. */ - const size_t arg_subcmd_start = arg_start + cmdsub_start, arg_subcmd_end = arg_start + cmdsub_end; + // Found a command substitution. Compute the position of the start and end of the cmdsub + // contents, within our overall src. + const size_t arg_subcmd_start = arg_start + cmdsub_start, + arg_subcmd_end = arg_start + cmdsub_end; - /* Highlight the parens. The open paren must exist; the closed paren may not if it was incomplete. */ + // Highlight the parens. The open paren must exist; the closed paren may not if it was + // incomplete. assert(cmdsub_start < arg_str.size()); this->color_array.at(arg_subcmd_start) = highlight_spec_operator; if (arg_subcmd_end < this->buff.size()) this->color_array.at(arg_subcmd_end) = highlight_spec_operator; - /* Compute the cursor's position within the cmdsub. We must be past the open paren (hence >) but can be at the end of the string or closed paren (hence <=) */ + // Compute the cursor's position within the cmdsub. We must be past the open paren (hence >) + // but can be at the end of the string or closed paren (hence <=). size_t cursor_subpos = CURSOR_POSITION_INVALID; - if (cursor_pos != CURSOR_POSITION_INVALID && cursor_pos > arg_subcmd_start && cursor_pos <= arg_subcmd_end) - { - /* The -1 because the cmdsub_contents does not include the open paren */ + if (cursor_pos != CURSOR_POSITION_INVALID && cursor_pos > arg_subcmd_start && + cursor_pos <= arg_subcmd_end) { + // The -1 because the cmdsub_contents does not include the open paren. cursor_subpos = cursor_pos - arg_subcmd_start - 1; } - /* Highlight it recursively. */ - highlighter_t cmdsub_highlighter(cmdsub_contents, cursor_subpos, this->vars, this->working_directory, this->io_ok); + // Highlight it recursively. + highlighter_t cmdsub_highlighter(cmdsub_contents, cursor_subpos, this->vars, + this->working_directory, this->io_ok); const color_array_t &subcolors = cmdsub_highlighter.highlight(); - /* Copy out the subcolors back into our array */ + // Copy out the subcolors back into our array. assert(subcolors.size() == cmdsub_contents.size()); - std::copy(subcolors.begin(), subcolors.end(), this->color_array.begin() + arg_subcmd_start + 1); + std::copy(subcolors.begin(), subcolors.end(), + this->color_array.begin() + arg_subcmd_start + 1); } } -// Indicates whether the source range of the given node forms a valid path in the given working_directory -static bool node_is_potential_path(const wcstring &src, const parse_node_t &node, const wcstring &working_directory) -{ - if (! node.has_source()) - return false; +/// Indicates whether the source range of the given node forms a valid path in the given +/// working_directory. +static bool node_is_potential_path(const wcstring &src, const parse_node_t &node, + const wcstring &working_directory) { + if (!node.has_source()) return false; - - /* Get the node source, unescape it, and then pass it to is_potential_path along with the working directory (as a one element list) */ + // Get the node source, unescape it, and then pass it to is_potential_path along with the + // working directory (as a one element list). bool result = false; wcstring token(src, node.source_start, node.source_length); - if (unescape_string_in_place(&token, UNESCAPE_SPECIAL)) - { - /* Big hack: is_potential_path expects a tilde, but unescape_string gives us HOME_DIRECTORY. Put it back. */ - if (! token.empty() && token.at(0) == HOME_DIRECTORY) - token.at(0) = L'~'; + if (unescape_string_in_place(&token, UNESCAPE_SPECIAL)) { + // Big hack: is_potential_path expects a tilde, but unescape_string gives us HOME_DIRECTORY. + // Put it back. + if (!token.empty() && token.at(0) == HOME_DIRECTORY) token.at(0) = L'~'; const wcstring_list_t working_directory_list(1, working_directory); result = is_potential_path(token, working_directory_list, PATH_EXPAND_TILDE); @@ -934,42 +788,39 @@ static bool node_is_potential_path(const wcstring &src, const parse_node_t &node return result; } -// Color all of the arguments of the given command -void highlighter_t::color_arguments(const parse_node_t &list_node) -{ - /* Hack: determine whether the parent is the cd command, so we can show errors for non-directories */ +// Color all of the arguments of the given command. +void highlighter_t::color_arguments(const parse_node_t &list_node) { + // Hack: determine whether the parent is the cd command, so we can show errors for + // non-directories. bool cmd_is_cd = false; - if (this->io_ok) - { + if (this->io_ok) { const parse_node_t *parent = this->parse_tree.get_parent(list_node, symbol_plain_statement); - if (parent != NULL) - { + if (parent != NULL) { wcstring cmd_str; - if (plain_statement_get_expanded_command(this->buff, this->parse_tree, *parent, &cmd_str)) - { + if (plain_statement_get_expanded_command(this->buff, this->parse_tree, *parent, + &cmd_str)) { cmd_is_cd = (cmd_str == L"cd"); } } } - /* Find all the arguments of this list */ - const parse_node_tree_t::parse_node_list_t nodes = this->parse_tree.find_nodes(list_node, symbol_argument); + // Find all the arguments of this list. + const parse_node_tree_t::parse_node_list_t nodes = + this->parse_tree.find_nodes(list_node, symbol_argument); - for (size_t i=0; i < nodes.size(); i++) - { + for (size_t i = 0; i < nodes.size(); i++) { const parse_node_t *child = nodes.at(i); assert(child != NULL && child->type == symbol_argument); this->color_argument(*child); - if (cmd_is_cd) - { - /* Mark this as an error if it's not 'help' and not a valid cd path */ + if (cmd_is_cd) { + // Mark this as an error if it's not 'help' and not a valid cd path. wcstring param = child->get_source(this->buff); - if (expand_one(param, EXPAND_SKIP_CMDSUBST)) - { - bool is_help = string_prefixes_string(param, L"--help") || string_prefixes_string(param, L"-h"); - if (! is_help && this->io_ok && ! is_potential_cd_path(param, working_directory, PATH_EXPAND_TILDE)) - { + if (expand_one(param, EXPAND_SKIP_CMDSUBST)) { + bool is_help = string_prefixes_string(param, L"--help") || + string_prefixes_string(param, L"-h"); + if (!is_help && this->io_ok && + !is_potential_cd_path(param, working_directory, PATH_EXPAND_TILDE)) { this->color_node(*child, highlight_spec_error); } } @@ -977,176 +828,172 @@ void highlighter_t::color_arguments(const parse_node_t &list_node) } } -void highlighter_t::color_redirection(const parse_node_t &redirection_node) -{ +void highlighter_t::color_redirection(const parse_node_t &redirection_node) { assert(redirection_node.type == symbol_redirection); - if (! redirection_node.has_source()) - return; + if (!redirection_node.has_source()) return; - const parse_node_t *redirection_primitive = this->parse_tree.get_child(redirection_node, 0, parse_token_type_redirection); //like 2> - const parse_node_t *redirection_target = this->parse_tree.get_child(redirection_node, 1, parse_token_type_string); //like &1 or file path + const parse_node_t *redirection_primitive = + this->parse_tree.get_child(redirection_node, 0, parse_token_type_redirection); // like 2> + const parse_node_t *redirection_target = this->parse_tree.get_child( + redirection_node, 1, parse_token_type_string); // like &1 or file path - if (redirection_primitive != NULL) - { + if (redirection_primitive != NULL) { wcstring target; - const enum token_type redirect_type = this->parse_tree.type_for_redirection(redirection_node, this->buff, NULL, &target); + const enum token_type redirect_type = + this->parse_tree.type_for_redirection(redirection_node, this->buff, NULL, &target); - /* We may get a TOK_NONE redirection type, e.g. if the redirection is invalid */ - this->color_node(*redirection_primitive, redirect_type == TOK_NONE ? highlight_spec_error : highlight_spec_redirection); + // We may get a TOK_NONE redirection type, e.g. if the redirection is invalid. + this->color_node(*redirection_primitive, redirect_type == TOK_NONE + ? highlight_spec_error + : highlight_spec_redirection); - /* Check if the argument contains a command substitution. If so, highlight it as a param even though it's a command redirection, and don't try to do any other validation. */ - if (parse_util_locate_cmdsubst(target.c_str(), NULL, NULL, true) != 0) - { - if (redirection_target != NULL) - { + // Check if the argument contains a command substitution. If so, highlight it as a param + // even though it's a command redirection, and don't try to do any other validation. + if (parse_util_locate_cmdsubst(target.c_str(), NULL, NULL, true) != 0) { + if (redirection_target != NULL) { this->color_argument(*redirection_target); } - } - else - { - /* No command substitution, so we can highlight the target file or fd. For example, disallow redirections into a non-existent directory */ + } else { + // No command substitution, so we can highlight the target file or fd. For example, + // disallow redirections into a non-existent directory. bool target_is_valid = true; - if (! this->io_ok) - { - /* I/O is disallowed, so we don't have much hope of catching anything but gross errors. Assume it's valid. */ + if (!this->io_ok) { + // I/O is disallowed, so we don't have much hope of catching anything but gross + // errors. Assume it's valid. target_is_valid = true; - } - else if (! expand_one(target, EXPAND_SKIP_CMDSUBST)) - { - /* Could not be expanded */ + } else if (!expand_one(target, EXPAND_SKIP_CMDSUBST)) { + // Could not be expanded. target_is_valid = false; - } - else - { - /* Ok, we successfully expanded our target. Now verify that it works with this redirection. We will probably need it as a path (but not in the case of fd redirections). Note that the target is now unescaped. */ - const wcstring target_path = path_apply_working_directory(target, this->working_directory); - switch (redirect_type) - { - case TOK_REDIRECT_FD: - { - /* target should be an fd. It must be all digits, and must not overflow. fish_wcstoi returns INT_MAX on overflow; we could instead check errno to disambiguiate this from a real INT_MAX fd, but instead we just disallow that. */ + } else { + // Ok, we successfully expanded our target. Now verify that it works with this + // redirection. We will probably need it as a path (but not in the case of fd + // redirections). Note that the target is now unescaped. + const wcstring target_path = + path_apply_working_directory(target, this->working_directory); + switch (redirect_type) { + case TOK_REDIRECT_FD: { + // Target should be an fd. It must be all digits, and must not overflow. + // fish_wcstoi returns INT_MAX on overflow; we could instead check errno to + // disambiguiate this from a real INT_MAX fd, but instead we just disallow + // that. const wchar_t *target_cstr = target.c_str(); wchar_t *end = NULL; int fd = fish_wcstoi(target_cstr, &end, 10); - /* The iswdigit check ensures there's no leading whitespace, the *end check ensures the entire string was consumed, and the numeric checks ensure the fd is at least zero and there was no overflow */ - target_is_valid = (iswdigit(target_cstr[0]) && *end == L'\0' && fd >= 0 && fd < INT_MAX); + // The iswdigit check ensures there's no leading whitespace, the *end check + // ensures the entire string was consumed, and the numeric checks ensure the + // fd is at least zero and there was no overflow. + target_is_valid = + (iswdigit(target_cstr[0]) && *end == L'\0' && fd >= 0 && fd < INT_MAX); + break; } - break; - - case TOK_REDIRECT_IN: - { - /* Input redirections must have a readable non-directory */ + case TOK_REDIRECT_IN: { + // Input redirections must have a readable non-directory. struct stat buf = {}; - target_is_valid = ! waccess(target_path, R_OK) && ! wstat(target_path, &buf) && ! S_ISDIR(buf.st_mode); + target_is_valid = !waccess(target_path, R_OK) && + !wstat(target_path, &buf) && !S_ISDIR(buf.st_mode); + break; } - break; - case TOK_REDIRECT_OUT: case TOK_REDIRECT_APPEND: - case TOK_REDIRECT_NOCLOB: - { - /* Test whether the file exists, and whether it's writable (possibly after creating it). access() returns failure if the file does not exist. */ + case TOK_REDIRECT_NOCLOB: { + // Test whether the file exists, and whether it's writable (possibly after + // creating it). access() returns failure if the file does not exist. bool file_exists = false, file_is_writable = false; int err = 0; struct stat buf = {}; - if (wstat(target_path, &buf) < 0) - { + if (wstat(target_path, &buf) < 0) { err = errno; } - if (string_suffixes_string(L"/", target)) - { - /* Redirections to things that are directories is definitely not allowed */ + if (string_suffixes_string(L"/", target)) { + // Redirections to things that are directories is definitely not + // allowed. file_exists = false; file_is_writable = false; - } - else if (err == 0) - { - /* No err. We can write to it if it's not a directory and we have permission */ + } else if (err == 0) { + // No err. We can write to it if it's not a directory and we have + // permission. file_exists = true; - file_is_writable = ! S_ISDIR(buf.st_mode) && ! waccess(target_path, W_OK); - } - else if (err == ENOENT) - { - /* File does not exist. Check if its parent directory is writable. */ + file_is_writable = !S_ISDIR(buf.st_mode) && !waccess(target_path, W_OK); + } else if (err == ENOENT) { + // File does not exist. Check if its parent directory is writable. wcstring parent = wdirname(target_path); - /* Ensure that the parent ends with the path separator. This will ensure that we get an error if the parent directory is not really a directory. */ - if (! string_suffixes_string(L"/", parent)) - parent.push_back(L'/'); + // Ensure that the parent ends with the path separator. This will ensure + // that we get an error if the parent directory is not really a + // directory. + if (!string_suffixes_string(L"/", parent)) parent.push_back(L'/'); - /* Now the file is considered writable if the parent directory is writable */ + // Now the file is considered writable if the parent directory is + // writable. file_exists = false; file_is_writable = (0 == waccess(parent, W_OK)); - } - else - { - /* Other errors we treat as not writable. This includes things like ENOTDIR. */ + } else { + // Other errors we treat as not writable. This includes things like + // ENOTDIR. file_exists = false; file_is_writable = false; } - /* NOCLOB means that we must not overwrite files that exist */ - target_is_valid = file_is_writable && !(file_exists && redirect_type == TOK_REDIRECT_NOCLOB); + // NOCLOB means that we must not overwrite files that exist. + target_is_valid = file_is_writable && + !(file_exists && redirect_type == TOK_REDIRECT_NOCLOB); + break; } - break; - - default: - /* We should not get here, since the node was marked as a redirection, but treat it as an error for paranoia */ + default: { + // We should not get here, since the node was marked as a redirection, but + // treat it as an error for paranoia. target_is_valid = false; break; + } } } - if (redirection_target != NULL) - { - this->color_node(*redirection_target, target_is_valid ? highlight_spec_redirection : highlight_spec_error); + if (redirection_target != NULL) { + this->color_node(*redirection_target, target_is_valid ? highlight_spec_redirection + : highlight_spec_error); } } } } -// Color all of the redirections of the given command -void highlighter_t::color_redirections(const parse_node_t &list_node) -{ - const parse_node_tree_t::parse_node_list_t nodes = this->parse_tree.find_nodes(list_node, symbol_redirection); - for (size_t i=0; i < nodes.size(); i++) - { +/// Color all of the redirections of the given command. +void highlighter_t::color_redirections(const parse_node_t &list_node) { + const parse_node_tree_t::parse_node_list_t nodes = + this->parse_tree.find_nodes(list_node, symbol_redirection); + for (size_t i = 0; i < nodes.size(); i++) { this->color_redirection(*nodes.at(i)); } } -/* Color all the children of the command with the given type */ -void highlighter_t::color_children(const parse_node_t &parent, parse_token_type_t type, highlight_spec_t color) -{ - for (node_offset_t idx=0; idx < parent.child_count; idx++) - { +/// Color all the children of the command with the given type. +void highlighter_t::color_children(const parse_node_t &parent, parse_token_type_t type, + highlight_spec_t color) { + for (node_offset_t idx = 0; idx < parent.child_count; idx++) { const parse_node_t *child = this->parse_tree.get_child(parent, idx); - if (child != NULL && child->type == type) - { + if (child != NULL && child->type == type) { this->color_node(*child, color); } } } -/* Determine if a command is valid */ -static bool command_is_valid(const wcstring &cmd, enum parse_statement_decoration_t decoration, const wcstring &working_directory, const env_vars_snapshot_t &vars) -{ - /* Determine which types we check, based on the decoration */ - bool builtin_ok = true, function_ok = true, abbreviation_ok = true, command_ok = true, implicit_cd_ok = true; - if (decoration == parse_statement_decoration_command || decoration == parse_statement_decoration_exec) - { +/// Determine if a command is valid. +static bool command_is_valid(const wcstring &cmd, enum parse_statement_decoration_t decoration, + const wcstring &working_directory, const env_vars_snapshot_t &vars) { + // Determine which types we check, based on the decoration. + bool builtin_ok = true, function_ok = true, abbreviation_ok = true, command_ok = true, + implicit_cd_ok = true; + if (decoration == parse_statement_decoration_command || + decoration == parse_statement_decoration_exec) { builtin_ok = false; function_ok = false; abbreviation_ok = false; command_ok = true; implicit_cd_ok = false; - } - else if (decoration == parse_statement_decoration_builtin) - { + } else if (decoration == parse_statement_decoration_builtin) { builtin_ok = true; function_ok = false; abbreviation_ok = false; @@ -1154,48 +1001,40 @@ static bool command_is_valid(const wcstring &cmd, enum parse_statement_decoratio implicit_cd_ok = false; } - /* Check them */ + // Check them. bool is_valid = false; - /* Builtins */ - if (! is_valid && builtin_ok) - is_valid = builtin_exists(cmd); + // Builtins + if (!is_valid && builtin_ok) is_valid = builtin_exists(cmd); - /* Functions */ - if (! is_valid && function_ok) - is_valid = function_exists_no_autoload(cmd, vars); + // Functions + if (!is_valid && function_ok) is_valid = function_exists_no_autoload(cmd, vars); - /* Abbreviations */ - if (! is_valid && abbreviation_ok) - is_valid = expand_abbreviation(cmd, NULL); + // Abbreviations + if (!is_valid && abbreviation_ok) is_valid = expand_abbreviation(cmd, NULL); - /* Regular commands */ - if (! is_valid && command_ok) - is_valid = path_get_path(cmd, NULL, vars); + // Regular commands + if (!is_valid && command_ok) is_valid = path_get_path(cmd, NULL, vars); - /* Implicit cd */ - if (! is_valid && implicit_cd_ok) + // Implicit cd + if (!is_valid && implicit_cd_ok) is_valid = path_can_be_implicit_cd(cmd, NULL, working_directory.c_str(), vars); - /* Return what we got */ + // Return what we got. return is_valid; } -const highlighter_t::color_array_t & highlighter_t::highlight() -{ - // If we are doing I/O, we must be in a background thread - if (io_ok) - { +const highlighter_t::color_array_t &highlighter_t::highlight() { + // If we are doing I/O, we must be in a background thread. + if (io_ok) { ASSERT_IS_BACKGROUND_THREAD(); } const size_t length = buff.size(); assert(this->buff.size() == this->color_array.size()); + if (length == 0) return color_array; - if (length == 0) - return color_array; - - /* Start out at zero */ + // Start out at zero. std::fill(this->color_array.begin(), this->color_array.end(), 0); #if 0 @@ -1203,14 +1042,13 @@ const highlighter_t::color_array_t & highlighter_t::highlight() fprintf(stderr, "%ls\n", dump.c_str()); #endif - /* Walk the node tree */ - for (parse_node_tree_t::const_iterator iter = parse_tree.begin(); iter != parse_tree.end(); ++iter) - { + // Walk the node tree. + for (parse_node_tree_t::const_iterator iter = parse_tree.begin(); iter != parse_tree.end(); + ++iter) { const parse_node_t &node = *iter; - switch (node.type) - { - // Color direct string descendants, e.g. 'for' and 'in'. + switch (node.type) { + // Color direct string descendants, e.g. 'for' and 'in'. case symbol_while_header: case symbol_begin_header: case symbol_function_header: @@ -1219,130 +1057,120 @@ const highlighter_t::color_array_t & highlighter_t::highlight() case symbol_case_item: case symbol_boolean_statement: case symbol_decorated_statement: - case symbol_if_statement: - { + case symbol_if_statement: { this->color_children(node, parse_token_type_string, highlight_spec_command); + break; } - break; - - case symbol_switch_statement: - { - const parse_node_t *literal_switch = this->parse_tree.get_child(node, 0, parse_token_type_string); - const parse_node_t *switch_arg = this->parse_tree.get_child(node, 1, symbol_argument); + case symbol_switch_statement: { + const parse_node_t *literal_switch = + this->parse_tree.get_child(node, 0, parse_token_type_string); + const parse_node_t *switch_arg = + this->parse_tree.get_child(node, 1, symbol_argument); this->color_node(*literal_switch, highlight_spec_command); this->color_node(*switch_arg, highlight_spec_param); + break; } - break; - - case symbol_for_header: - { - // Color the 'for' and 'in' as commands - const parse_node_t *literal_for_node = this->parse_tree.get_child(node, 0, parse_token_type_string); - const parse_node_t *literal_in_node = this->parse_tree.get_child(node, 2, parse_token_type_string); + case symbol_for_header: { + // Color the 'for' and 'in' as commands. + const parse_node_t *literal_for_node = + this->parse_tree.get_child(node, 0, parse_token_type_string); + const parse_node_t *literal_in_node = + this->parse_tree.get_child(node, 2, parse_token_type_string); this->color_node(*literal_for_node, highlight_spec_command); this->color_node(*literal_in_node, highlight_spec_command); - // Color the variable name as a parameter - const parse_node_t *var_name_node = this->parse_tree.get_child(node, 1, parse_token_type_string); + // Color the variable name as a parameter. + const parse_node_t *var_name_node = + this->parse_tree.get_child(node, 1, parse_token_type_string); this->color_argument(*var_name_node); + break; } - break; - case parse_token_type_pipe: case parse_token_type_background: case parse_token_type_end: - case symbol_optional_background: - { + case symbol_optional_background: { this->color_node(node, highlight_spec_statement_terminator); + break; } - break; + case symbol_plain_statement: { + // Get the decoration from the parent. + enum parse_statement_decoration_t decoration = + parse_tree.decoration_for_plain_statement(node); - case symbol_plain_statement: - { - /* Get the decoration from the parent */ - enum parse_statement_decoration_t decoration = parse_tree.decoration_for_plain_statement(node); - - /* Color the command */ - const parse_node_t *cmd_node = parse_tree.get_child(node, 0, parse_token_type_string); - if (cmd_node != NULL && cmd_node->has_source()) - { + // Color the command. + const parse_node_t *cmd_node = + parse_tree.get_child(node, 0, parse_token_type_string); + if (cmd_node != NULL && cmd_node->has_source()) { bool is_valid_cmd = false; - if (! this->io_ok) - { - /* We cannot check if the command is invalid, so just assume it's valid */ + if (!this->io_ok) { + // We cannot check if the command is invalid, so just assume it's valid. is_valid_cmd = true; - } - else - { - /* Check to see if the command is valid */ + } else { + // Check to see if the command is valid. wcstring cmd(buff, cmd_node->source_start, cmd_node->source_length); - /* Try expanding it. If we cannot, it's an error. */ - bool expanded = expand_one(cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES | EXPAND_SKIP_JOBS); - if (expanded && ! has_expand_reserved(cmd)) - { - is_valid_cmd = command_is_valid(cmd, decoration, working_directory, vars); + // Try expanding it. If we cannot, it's an error. + bool expanded = expand_one( + cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES | EXPAND_SKIP_JOBS); + if (expanded && !has_expand_reserved(cmd)) { + is_valid_cmd = + command_is_valid(cmd, decoration, working_directory, vars); } } - this->color_node(*cmd_node, is_valid_cmd ? highlight_spec_command : highlight_spec_error); + this->color_node(*cmd_node, + is_valid_cmd ? highlight_spec_command : highlight_spec_error); } + break; } - break; - - case symbol_arguments_or_redirections_list: - case symbol_argument_list: - { - /* Only work on root lists, so that we don't re-color child lists */ - if (parse_tree.argument_list_is_root(node)) - { + case symbol_argument_list: { + // Only work on root lists, so that we don't re-color child lists. + if (parse_tree.argument_list_is_root(node)) { this->color_arguments(node); this->color_redirections(node); } + break; } - break; - - case symbol_end_command: + case symbol_end_command: { this->color_node(node, highlight_spec_command); break; - + } case parse_special_type_parse_error: - case parse_special_type_tokenizer_error: + case parse_special_type_tokenizer_error: { this->color_node(node, highlight_spec_error); break; - - case parse_special_type_comment: + } + case parse_special_type_comment: { this->color_node(node, highlight_spec_comment); break; - - default: - break; + } + default: { break; } } } - if (this->io_ok && this->cursor_pos <= this->buff.size()) - { - /* If the cursor is over an argument, and that argument is a valid path, underline it */ - for (parse_node_tree_t::const_iterator iter = parse_tree.begin(); iter != parse_tree.end(); ++iter) - { + if (this->io_ok && this->cursor_pos <= this->buff.size()) { + // If the cursor is over an argument, and that argument is a valid path, underline it. + for (parse_node_tree_t::const_iterator iter = parse_tree.begin(); iter != parse_tree.end(); + ++iter) { const parse_node_t &node = *iter; - /* Must be an argument with source */ - if (node.type != symbol_argument || ! node.has_source()) - continue; + // Must be an argument with source. + if (node.type != symbol_argument || !node.has_source()) continue; - /* See if this node contains the cursor. We check <= source_length so that, when backspacing (and the cursor is just beyond the last token), we may still underline it */ - if (this->cursor_pos >= node.source_start && this->cursor_pos - node.source_start <= node.source_length) - { - /* See if this is a valid path */ - if (node_is_potential_path(buff, node, working_directory)) - { - /* It is, underline it. */ - for (size_t i=node.source_start; i < node.source_start + node.source_length; i++) - { - /* Don't color highlight_spec_error because it looks dorky. For example, trying to cd into a non-directory would show an underline and also red. */ - if (highlight_get_primary(this->color_array.at(i)) != highlight_spec_error) - { + // See if this node contains the cursor. We check <= source_length so that, when + // backspacing (and the cursor is just beyond the last token), we may still underline + // it. + if (this->cursor_pos >= node.source_start && + this->cursor_pos - node.source_start <= node.source_length) { + // See if this is a valid path. + if (node_is_potential_path(buff, node, working_directory)) { + // It is, underline it. + for (size_t i = node.source_start; i < node.source_start + node.source_length; + i++) { + // Don't color highlight_spec_error because it looks dorky. For example, + // trying to cd into a non-directory would show an underline and also red. + if (highlight_get_primary(this->color_array.at(i)) != + highlight_spec_error) { this->color_array.at(i) |= highlight_modifier_valid_path; } } @@ -1354,139 +1182,113 @@ const highlighter_t::color_array_t & highlighter_t::highlight() return color_array; } -void highlight_shell(const wcstring &buff, std::vector &color, size_t pos, wcstring_list_t *error, const env_vars_snapshot_t &vars) -{ - /* Do something sucky and get the current working directory on this background thread. This should really be passed in. */ +void highlight_shell(const wcstring &buff, std::vector &color, size_t pos, + wcstring_list_t *error, const env_vars_snapshot_t &vars) { + // Do something sucky and get the current working directory on this background thread. This + // should really be passed in. const wcstring working_directory = env_get_pwd_slash(); - /* Highlight it! */ + // Highlight it! highlighter_t highlighter(buff, pos, vars, working_directory, true /* can do IO */); color = highlighter.highlight(); } -void highlight_shell_no_io(const wcstring &buff, std::vector &color, size_t pos, wcstring_list_t *error, const env_vars_snapshot_t &vars) -{ - /* Do something sucky and get the current working directory on this background thread. This should really be passed in. */ +void highlight_shell_no_io(const wcstring &buff, std::vector &color, size_t pos, + wcstring_list_t *error, const env_vars_snapshot_t &vars) { + // Do something sucky and get the current working directory on this background thread. This + // should really be passed in. const wcstring working_directory = env_get_pwd_slash(); - /* Highlight it! */ + // Highlight it! highlighter_t highlighter(buff, pos, vars, working_directory, false /* no IO allowed */); color = highlighter.highlight(); } -/** - Perform quote and parenthesis highlighting on the specified string. -*/ -static void highlight_universal_internal(const wcstring &buffstr, std::vector &color, size_t pos) -{ +/// Perform quote and parenthesis highlighting on the specified string. +static void highlight_universal_internal(const wcstring &buffstr, + std::vector &color, size_t pos) { assert(buffstr.size() == color.size()); - if (pos < buffstr.size()) - { - - /* - Highlight matching quotes - */ - if ((buffstr.at(pos) == L'\'') || (buffstr.at(pos) == L'\"')) - { + if (pos < buffstr.size()) { + // Highlight matching quotes. + if ((buffstr.at(pos) == L'\'') || (buffstr.at(pos) == L'\"')) { std::vector lst; - - int level=0; - wchar_t prev_q=0; - - const wchar_t * const buff = buffstr.c_str(); + int level = 0; + wchar_t prev_q = 0; + const wchar_t *const buff = buffstr.c_str(); const wchar_t *str = buff; + int match_found = 0; - int match_found=0; - - while (*str) - { - switch (*str) - { - case L'\\': + while (*str) { + switch (*str) { + case L'\\': { str++; break; + } case L'\"': - case L'\'': - if (level == 0) - { + case L'\'': { + if (level == 0) { level++; - lst.push_back(str-buff); + lst.push_back(str - buff); prev_q = *str; - } - else - { - if (prev_q == *str) - { + } else { + if (prev_q == *str) { size_t pos1, pos2; level--; pos1 = lst.back(); - pos2 = str-buff; - if (pos1==pos || pos2==pos) - { - color.at(pos1)|=highlight_make_background(highlight_spec_match); - color.at(pos2)|=highlight_make_background(highlight_spec_match); + pos2 = str - buff; + if (pos1 == pos || pos2 == pos) { + color.at(pos1) |= + highlight_make_background(highlight_spec_match); + color.at(pos2) |= + highlight_make_background(highlight_spec_match); match_found = 1; - } - prev_q = *str==L'\"'?L'\'':L'\"'; - } - else - { + prev_q = *str == L'\"' ? L'\'' : L'\"'; + } else { level++; - lst.push_back(str-buff); + lst.push_back(str - buff); prev_q = *str; } } - break; + } } - if ((*str == L'\0')) - break; - + if ((*str == L'\0')) break; str++; } - if (!match_found) - color.at(pos) = highlight_make_background(highlight_spec_error); + if (!match_found) color.at(pos) = highlight_make_background(highlight_spec_error); } - /* - Highlight matching parenthesis - */ + // Highlight matching parenthesis. const wchar_t c = buffstr.at(pos); - if (wcschr(L"()[]{}", c)) - { - int step = wcschr(L"({[", c)?1:-1; + 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) - { + 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 (test_char == inc_char) level++; + if (test_char == dec_char) level--; + if (level == 0) { long pos2 = i; - color.at(pos)|=highlight_spec_match<<16; - color.at(pos2)|=highlight_spec_match<<16; - match_found=1; + color.at(pos) |= highlight_spec_match << 16; + color.at(pos2) |= highlight_spec_match << 16; + match_found = 1; break; } } - if (!match_found) - color[pos] = highlight_make_background(highlight_spec_error); + if (!match_found) color[pos] = highlight_make_background(highlight_spec_error); } } } -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); diff --git a/src/highlight.h b/src/highlight.h index 46149f464..fa62010a4 100644 --- a/src/highlight.h +++ b/src/highlight.h @@ -1,39 +1,38 @@ -/** \file highlight.h - Prototypes for functions for syntax highlighting -*/ - +// Prototypes for functions for syntax highlighting. #ifndef FISH_HIGHLIGHT_H #define FISH_HIGHLIGHT_H #include +#include #include #include -#include +#include "color.h" #include "common.h" #include "env.h" -#include "color.h" -/* Internally, we specify highlight colors using a set of bits. Each highlight_spec is a 32 bit uint. We divide this into low 16 (foreground) and high 16 (background). Each half we further subdivide into low 8 (primary) and high 8 (modifiers). The primary is not a bitmask; specify exactly one. The modifiers are a bitmask; specify any number */ -enum -{ - /* The following values are mutually exclusive; specify at most one */ - highlight_spec_normal = 0, // normal text - highlight_spec_error, // error - highlight_spec_command, //command - highlight_spec_statement_terminator, //process separator - highlight_spec_param, //command parameter (argument) - highlight_spec_comment, //comment - highlight_spec_match, //matching parenthesis, etc. - highlight_spec_search_match, //search match - highlight_spec_operator, //operator - highlight_spec_escape, //escape sequences - highlight_spec_quote, //quoted string - highlight_spec_redirection, //redirection - highlight_spec_autosuggestion, //autosuggestion +// Internally, we specify highlight colors using a set of bits. Each highlight_spec is a 32 bit +// uint. We divide this into low 16 (foreground) and high 16 (background). Each half we further +// subdivide into low 8 (primary) and high 8 (modifiers). The primary is not a bitmask; specify +// exactly one. The modifiers are a bitmask; specify any number. +enum { + // The following values are mutually exclusive; specify at most one. + highlight_spec_normal = 0, // normal text + highlight_spec_error, // error + highlight_spec_command, // command + highlight_spec_statement_terminator, // process separator + highlight_spec_param, // command parameter (argument) + highlight_spec_comment, // comment + highlight_spec_match, // matching parenthesis, etc. + highlight_spec_search_match, // search match + highlight_spec_operator, // operator + highlight_spec_escape, // escape sequences + highlight_spec_quote, // quoted string + highlight_spec_redirection, // redirection + highlight_spec_autosuggestion, // autosuggestion highlight_spec_selection, - // Pager support + // Pager support. highlight_spec_pager_prefix, highlight_spec_pager_completion, highlight_spec_pager_description, @@ -42,91 +41,91 @@ enum HIGHLIGHT_SPEC_PRIMARY_MASK = 0xFF, - /* The following values are modifiers */ + // The following values are modifiers. highlight_modifier_valid_path = 0x100, highlight_modifier_force_underline = 0x200, - highlight_modifier_sloppy_background = 0x300, //hackish, indicates that we should treat a foreground color as background, per certain historical behavior - + // Hackish, indicates that we should treat a foreground color as background, per certain + // historical behavior. + highlight_modifier_sloppy_background = 0x300, /* Very special value */ highlight_spec_invalid = 0xFFFF }; typedef uint32_t highlight_spec_t; -inline highlight_spec_t highlight_get_primary(highlight_spec_t val) -{ +inline highlight_spec_t highlight_get_primary(highlight_spec_t val) { return val & HIGHLIGHT_SPEC_PRIMARY_MASK; } -inline highlight_spec_t highlight_make_background(highlight_spec_t val) -{ - assert(val >> 16 == 0); //should have nothing in upper bits, otherwise this is already a background +inline highlight_spec_t highlight_make_background(highlight_spec_t val) { + assert(val >> 16 == + 0); // should have nothing in upper bits, otherwise this is already a background return val << 16; } class history_item_t; struct file_detection_context_t; -/** - Perform syntax highlighting for the shell commands in buff. The result is - stored in the color array as a color_code from the HIGHLIGHT_ enum - for each character in buff. +/// Perform syntax highlighting for the shell commands in buff. The result is stored in the color +/// array as a color_code from the HIGHLIGHT_ enum for each character in buff. +/// +/// \param buff The buffer on which to perform syntax highlighting +/// \param color The array in wchich to store the color codes. The first 8 bits are used for fg +/// color, the next 8 bits for bg color. +/// \param 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); - \param buff The buffer on which to perform syntax highlighting - \param color The array in wchich to store the color codes. The first 8 bits are used for fg color, the next 8 bits for bg color. - \param 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); +/// Perform a non-blocking shell highlighting. The function will not do any I/O that may block. As a +/// result, invalid commands may not be detected, etc. +void highlight_shell_no_io(const wcstring &buffstr, std::vector &color, + size_t pos, wcstring_list_t *error, const env_vars_snapshot_t &vars); -/** - Perform a non-blocking shell highlighting. The function will not do any I/O that may block. As a result, invalid commands may not be detected, etc. -*/ -void highlight_shell_no_io(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 stored in the color array as a color_code from the HIGHLIGHT_ enum +/// for each character in buff. +/// +/// \param buff The buffer on which to perform syntax highlighting +/// \param color The array in wchich to store the color codes. The first 8 bits are used for fg +/// color, the next 8 bits for bg color. +/// \param 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); -/** - Perform syntax highlighting for the text in buff. Matching quotes and paranthesis are highlighted. The result is - stored in the color array as a color_code from the HIGHLIGHT_ enum - for each character in buff. - - \param buff The buffer on which to perform syntax highlighting - \param color The array in wchich to store the color codes. The first 8 bits are used for fg color, the next 8 bits for bg color. - \param 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); - -/** - Translate from HIGHLIGHT_* to FISH_COLOR_* according to environment - variables. Defaults to FISH_COLOR_NORMAL. - - Example: - - If the environment variable FISH_FISH_COLOR_ERROR is set to 'red', a - call to highlight_get_color( highlight_error) will return - FISH_COLOR_RED. -*/ +/// Translate from HIGHLIGHT_* to FISH_COLOR_* according to environment variables. Defaults to +/// FISH_COLOR_NORMAL. +/// +/// Example: +/// +/// If the environment variable FISH_FISH_COLOR_ERROR is set to 'red', a call to +/// highlight_get_color( highlight_error) will return FISH_COLOR_RED. rgb_color_t highlight_get_color(highlight_spec_t 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. -*/ -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); +/// 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. +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); -/* Tests whether the specified string cpath is the prefix of anything we could cd to. directories is a list of possible parent directories (typically either the working directory, or the cdpath). This does I/O! - - This is used only internally to this file, and is exposed only for testing. -*/ -enum -{ - /* The path must be to a directory */ +// Tests whether the specified string cpath is the prefix of anything we could cd to. directories is +// a list of possible parent directories (typically either the working directory, or the cdpath). +// This does I/O! +// +// This is used only internally to this file, and is exposed only for testing. +enum { + // The path must be to a directory. PATH_REQUIRE_DIR = 1 << 0, - - /* Expand any leading tilde in the path */ + // Expand any leading tilde in the path. PATH_EXPAND_TILDE = 1 << 1 }; typedef unsigned int path_flags_t; -bool is_potential_path(const wcstring &const_path, const wcstring_list_t &directories, path_flags_t flags); +bool is_potential_path(const wcstring &const_path, const wcstring_list_t &directories, + path_flags_t flags); #endif - From 45c6ac02088873d2b151d8a550129209f7f13da8 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sat, 30 Apr 2016 21:31:25 -0700 Subject: [PATCH 188/363] restyle input module to match project style Reduces lint errors from 69 to 48 (-30%). Line count from 1270 to 1044 (-18%). Another step in resolving issue #2902. --- src/input.cpp | 945 ++++++++++++++++++++------------------------------ src/input.h | 127 +++---- 2 files changed, 423 insertions(+), 649 deletions(-) diff --git a/src/input.cpp b/src/input.cpp index c45939bc4..d37b963c0 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -1,7 +1,4 @@ -/** \file input.c - - Functions for reading a character of input from stdin. -*/ +// Functions for reading a character of input from stdin. #include "config.h" #include @@ -21,288 +18,243 @@ #include #endif #include -#include #include #include #include +#include -#include "fallback.h" // IWYU pragma: keep -#include "wutil.h" // IWYU pragma: keep -#include "reader.h" -#include "proc.h" #include "common.h" -#include "input_common.h" -#include "input.h" -#include "parser.h" #include "env.h" #include "event.h" -#include "signal.h" // IWYU pragma: keep +#include "fallback.h" // IWYU pragma: keep +#include "input.h" +#include "input_common.h" #include "io.h" #include "output.h" +#include "parser.h" +#include "proc.h" +#include "reader.h" +#include "signal.h" // IWYU pragma: keep +#include "wutil.h" // IWYU pragma: keep #define DEFAULT_TERM L"ansi" #define MAX_INPUT_FUNCTION_ARGS 20 -/** - Struct representing a keybinding. Returned by input_get_mappings. - */ - -struct input_mapping_t -{ - wcstring seq; /**< Character sequence which generates this event */ - wcstring_list_t commands; /**< commands that should be evaluated by this mapping */ - - /* We wish to preserve the user-specified order. This is just an incrementing value. */ +/// Struct representing a keybinding. Returned by input_get_mappings. +struct input_mapping_t { + /// Character sequence which generates this event. + wcstring seq; + /// Commands that should be evaluated by this mapping. + wcstring_list_t commands; + /// We wish to preserve the user-specified order. This is just an incrementing value. unsigned int specification_order; - - wcstring mode; /**< mode in which this command should be evaluated */ - wcstring sets_mode; /** new mode that should be switched to after command evaluation */ + /// Mode in which this command should be evaluated. + wcstring mode; + /// New mode that should be switched to after command evaluation. + wcstring sets_mode; input_mapping_t(const wcstring &s, const std::vector &c, - const wcstring &m = DEFAULT_BIND_MODE, - const wcstring &sm = DEFAULT_BIND_MODE) : seq(s), commands(c), mode(m), sets_mode(sm) - { + const wcstring &m = DEFAULT_BIND_MODE, const wcstring &sm = DEFAULT_BIND_MODE) + : seq(s), commands(c), mode(m), sets_mode(sm) { static unsigned int s_last_input_mapping_specification_order = 0; specification_order = ++s_last_input_mapping_specification_order; - } }; -/** - A struct representing the mapping from a terminfo key name to a terminfo character sequence - */ -struct terminfo_mapping_t -{ - const wchar_t *name; /**< Name of key */ - const char *seq; /**< Character sequence generated on keypress. Constant string. */ +/// A struct representing the mapping from a terminfo key name to a terminfo character sequence. +struct terminfo_mapping_t { + const wchar_t *name; // name of key + const char *seq; // character sequence generated on keypress }; +/// Names of all the input functions supported. +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"forward-bigword", + L"backward-bigword", + 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"complete-and-search", + L"beginning-of-history", + L"end-of-history", + L"backward-kill-line", + L"kill-whole-line", + L"kill-word", + L"kill-bigword", + L"backward-kill-word", + L"backward-kill-path-component", + L"backward-kill-bigword", + L"history-token-search-backward", + L"history-token-search-forward", + L"self-insert", + L"transpose-chars", + L"transpose-words", + L"upcase-word", + L"downcase-word", + L"capitalize-word", + L"vi-arg-digit", + L"vi-delete-to", + L"execute", + L"beginning-of-buffer", + L"end-of-buffer", + L"repaint", + L"force-repaint", + L"up-line", + L"down-line", + L"suppress-autosuggestion", + L"accept-autosuggestion", + L"begin-selection", + L"swap-selection-start-stop", + L"end-selection", + L"kill-selection", + L"forward-jump", + L"backward-jump", + L"and", + L"cancel"}; -/** - Names of all the input functions supported -*/ -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"forward-bigword", - L"backward-bigword", - 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"complete-and-search", - L"beginning-of-history", - L"end-of-history", - L"backward-kill-line", - L"kill-whole-line", - L"kill-word", - L"kill-bigword", - L"backward-kill-word", - L"backward-kill-path-component", - L"backward-kill-bigword", - L"history-token-search-backward", - L"history-token-search-forward", - L"self-insert", - L"transpose-chars", - L"transpose-words", - L"upcase-word", - L"downcase-word", - L"capitalize-word", - L"vi-arg-digit", - L"vi-delete-to", - L"execute", - L"beginning-of-buffer", - L"end-of-buffer", - L"repaint", - L"force-repaint", - L"up-line", - L"down-line", - L"suppress-autosuggestion", - L"accept-autosuggestion", - L"begin-selection", - L"swap-selection-start-stop", - L"end-selection", - L"kill-selection", - L"forward-jump", - L"backward-jump", - L"and", - L"cancel" -}; - -wcstring describe_char(wint_t c) -{ +wcstring describe_char(wint_t c) { wint_t initial_cmd_char = R_BEGINNING_OF_LINE; size_t name_count = sizeof name_arr / sizeof *name_arr; - if (c >= initial_cmd_char && c < initial_cmd_char + name_count) - { + if (c >= initial_cmd_char && c < initial_cmd_char + name_count) { return format_string(L"%02x (%ls)", c, name_arr[c - initial_cmd_char]); } return format_string(L"%02x", c); } -/** - Description of each supported input function -*/ -/* -static const wchar_t *desc_arr[] = -{ - L"Move to beginning of line", - L"Move to end of line", - L"Move forward one character", - L"Move backward one character", - L"Move forward one word", - L"Move backward one word", - L"Search backward through list of previous commands", - L"Search forward through list of previous commands", - L"Delete one character forward", - L"Delete one character backward", - L"Move contents from cursor to end of line to killring", - L"Paste contents of killring", - L"Rotate to previous killring entry", - L"Guess the rest of the next input token", - L"Move to first item of history", - L"Move to last item of history", - L"Clear current line", - L"Move contents from beginning of line to cursor to killring", - L"Move entire line to killring", - L"Move next word to killring", - L"Move previous word to killring", - L"Write out key bindings", - L"Clear entire screen", - L"Quit the running program", - L"Search backward through list of previous commands for matching token", - L"Search forward through list of previous commands for matching token", - L"Insert the pressed key", - L"Do nothing", - L"End of file", - L"Repeat command" -} - ; -*/ +/// Description of each supported input function. +static const wchar_t *desc_arr[] = { + L"Move to beginning of line", + L"Move to end of line", + L"Move forward one character", + L"Move backward one character", + L"Move forward one word", + L"Move backward one word", + L"Search backward through list of previous commands", + L"Search forward through list of previous commands", + L"Delete one character forward", + L"Delete one character backward", + L"Move contents from cursor to end of line to killring", + L"Paste contents of killring", + L"Rotate to previous killring entry", + L"Guess the rest of the next input token", + L"Move to first item of history", + L"Move to last item of history", + L"Clear current line", + L"Move contents from beginning of line to cursor to killring", + L"Move entire line to killring", + L"Move next word to killring", + L"Move previous word to killring", + L"Write out key bindings", + L"Clear entire screen", + L"Quit the running program", + L"Search backward through list of previous commands for matching token", + L"Search forward through list of previous commands for matching token", + L"Insert the pressed key", + L"Do nothing", + L"End of file", + L"Repeat command"}; -/** - Internal code for each supported input function -*/ -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_FORWARD_BIGWORD, - R_BACKWARD_BIGWORD, - 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_COMPLETE_AND_SEARCH, - R_BEGINNING_OF_HISTORY, - R_END_OF_HISTORY, - R_BACKWARD_KILL_LINE, - R_KILL_WHOLE_LINE, - R_KILL_WORD, - R_KILL_BIGWORD, - R_BACKWARD_KILL_WORD, - R_BACKWARD_KILL_PATH_COMPONENT, - R_BACKWARD_KILL_BIGWORD, - R_HISTORY_TOKEN_SEARCH_BACKWARD, - R_HISTORY_TOKEN_SEARCH_FORWARD, - R_SELF_INSERT, - R_TRANSPOSE_CHARS, - R_TRANSPOSE_WORDS, - R_UPCASE_WORD, - R_DOWNCASE_WORD, - R_CAPITALIZE_WORD, - R_VI_ARG_DIGIT, - R_VI_DELETE_TO, - R_EXECUTE, - R_BEGINNING_OF_BUFFER, - R_END_OF_BUFFER, - R_REPAINT, - R_FORCE_REPAINT, - R_UP_LINE, - R_DOWN_LINE, - R_SUPPRESS_AUTOSUGGESTION, - R_ACCEPT_AUTOSUGGESTION, - R_BEGIN_SELECTION, - R_SWAP_SELECTION_START_STOP, - R_END_SELECTION, - R_KILL_SELECTION, - R_FORWARD_JUMP, - R_BACKWARD_JUMP, - R_AND, - R_CANCEL -}; +/// Internal code for each supported input function. +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_FORWARD_BIGWORD, + R_BACKWARD_BIGWORD, + 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_COMPLETE_AND_SEARCH, + R_BEGINNING_OF_HISTORY, + R_END_OF_HISTORY, + R_BACKWARD_KILL_LINE, + R_KILL_WHOLE_LINE, + R_KILL_WORD, + R_KILL_BIGWORD, + R_BACKWARD_KILL_WORD, + R_BACKWARD_KILL_PATH_COMPONENT, + R_BACKWARD_KILL_BIGWORD, + R_HISTORY_TOKEN_SEARCH_BACKWARD, + R_HISTORY_TOKEN_SEARCH_FORWARD, + R_SELF_INSERT, + R_TRANSPOSE_CHARS, + R_TRANSPOSE_WORDS, + R_UPCASE_WORD, + R_DOWNCASE_WORD, + R_CAPITALIZE_WORD, + R_VI_ARG_DIGIT, + R_VI_DELETE_TO, + R_EXECUTE, + R_BEGINNING_OF_BUFFER, + R_END_OF_BUFFER, + R_REPAINT, + R_FORCE_REPAINT, + R_UP_LINE, + R_DOWN_LINE, + R_SUPPRESS_AUTOSUGGESTION, + R_ACCEPT_AUTOSUGGESTION, + R_BEGIN_SELECTION, + R_SWAP_SELECTION_START_STOP, + R_END_SELECTION, + R_KILL_SELECTION, + R_FORWARD_JUMP, + R_BACKWARD_JUMP, + R_AND, + R_CANCEL}; -/** Mappings for the current input mode */ +/// Mappings for the current input mode. static std::vector mapping_list; -/* Terminfo map list */ +/// Terminfo map list. static std::vector terminfo_mappings; -#define TERMINFO_ADD(key) { (L ## #key) + 4, key } +#define TERMINFO_ADD(key) \ + { (L## #key) + 4, key } - -/** - List of all terminfo mappings - */ +/// List of all terminfo mappings. static std::vector mappings; - -/** - Set to one when the input subsytem has been initialized. -*/ +/// Set to one when the input subsytem has been initialized. static bool is_init = false; -/** - Initialize terminfo. - */ +/// Initialize terminfo. static void input_terminfo_init(); static wchar_t input_function_args[MAX_INPUT_FUNCTION_ARGS]; static bool input_function_status; static int input_function_args_index = 0; -/** - Return the current bind mode -*/ -wcstring input_get_bind_mode() -{ +/// Return the current bind mode. +wcstring input_get_bind_mode() { env_var_t mode = env_get_string(FISH_BIND_MODE_VAR); return mode.missing() ? DEFAULT_BIND_MODE : mode; } -/** - Set the current bind mode -*/ -void input_set_bind_mode(const wcstring &bm) -{ +/// Set the current bind mode. +void input_set_bind_mode(const wcstring &bm) { env_set(FISH_BIND_MODE_VAR, bm.c_str(), ENV_GLOBAL); } - -/** - Returns the arity of a given input function -*/ -int input_function_arity(int function) -{ - switch (function) - { +/// Returns the arity of a given input function. +int input_function_arity(int function) { + switch (function) { case R_FORWARD_JUMP: case R_BACKWARD_JUMP: return 1; @@ -311,153 +263,115 @@ int input_function_arity(int function) } } -/** - Sets the return status of the most recently executed input function -*/ -void input_function_set_status(bool status) -{ - input_function_status = status; -} +/// Sets the return status of the most recently executed input function. +void input_function_set_status(bool status) { input_function_status = status; } -/* Helper function to compare the lengths of sequences */ -static bool length_is_greater_than(const input_mapping_t &m1, const input_mapping_t &m2) -{ +/// Helper function to compare the lengths of sequences. +static bool length_is_greater_than(const input_mapping_t &m1, const input_mapping_t &m2) { return m1.seq.size() > m2.seq.size(); } -static bool specification_order_is_less_than(const input_mapping_t &m1, const input_mapping_t &m2) -{ +static bool specification_order_is_less_than(const input_mapping_t &m1, const input_mapping_t &m2) { return m1.specification_order < m2.specification_order; } -/* Inserts an input mapping at the correct position. We sort them in descending order by length, so that we test longer sequences first. */ -static void input_mapping_insert_sorted(const input_mapping_t &new_mapping) -{ - std::vector::iterator loc = std::lower_bound(mapping_list.begin(), mapping_list.end(), new_mapping, length_is_greater_than); +/// Inserts an input mapping at the correct position. We sort them in descending order by length, so +/// that we test longer sequences first. +static void input_mapping_insert_sorted(const input_mapping_t &new_mapping) { + std::vector::iterator loc = std::lower_bound( + mapping_list.begin(), mapping_list.end(), new_mapping, length_is_greater_than); mapping_list.insert(loc, new_mapping); } -/* Adds an input mapping */ -void input_mapping_add(const wchar_t *sequence, const wchar_t * const *commands, size_t commands_len, - const wchar_t *mode, const wchar_t *sets_mode) -{ - CHECK(sequence,); - CHECK(commands,); - CHECK(mode,); - CHECK(sets_mode,); +/// Adds an input mapping. +void input_mapping_add(const wchar_t *sequence, const wchar_t *const *commands, size_t commands_len, + const wchar_t *mode, const wchar_t *sets_mode) { + CHECK(sequence, ); + CHECK(commands, ); + CHECK(mode, ); + CHECK(sets_mode, ); - // debug( 0, L"Add mapping from %ls to %ls in mode %ls", escape(sequence, ESCAPE_ALL).c_str(), escape(command, ESCAPE_ALL).c_str(), mode); + // debug( 0, L"Add mapping from %ls to %ls in mode %ls", escape(sequence, ESCAPE_ALL).c_str(), + // escape(command, ESCAPE_ALL).c_str(), mode); - // remove existing mappings with this sequence + // Remove existing mappings with this sequence. const wcstring_list_t commands_vector(commands, commands + commands_len); - for (size_t 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) - { - /* Explicitly supported */ + } else if (term.find(L"256color") != wcstring::npos) { + // Explicitly supported. support_term256 = true; - } - else if (term.find(L"xterm") != wcstring::npos) - { - // assume that all xterms are 256, except for OS X SnowLeopard + } 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 - { - // Don't know, default to false + } else { + // Don't know, default to false. support_term256 = false; } } env_var_t fish_term24bit = env_get_string(L"fish_term24bit"); bool support_term24bit; - if (! fish_term24bit.missing_or_empty()) - { + if (!fish_term24bit.missing_or_empty()) { support_term24bit = from_string(fish_term24bit); - } - else - { - /* We don't attempt to infer term24 bit support yet. */ + } else { + // We don't attempt to infer term24 bit support yet. support_term24bit = false; } - color_support_t support = (support_term256 ? color_support_term256 : 0) | (support_term24bit ? color_support_term24bit : 0); + color_support_t support = (support_term256 ? color_support_term256 : 0) | + (support_term24bit ? color_support_term24bit : 0); output_set_color_support(support); } -int input_init() -{ - if (is_init) - return 1; +int input_init() { + if (is_init) return 1; is_init = true; @@ -465,37 +379,32 @@ int input_init() const env_var_t term = env_get_string(L"TERM"); int errret; - if (setupterm(const_cast(wcs2string(term).c_str()), STDOUT_FILENO, &errret) == ERR) - { + if (setupterm(const_cast(wcs2string(term).c_str()), STDOUT_FILENO, &errret) == ERR) { debug(0, _(L"Could not set up terminal")); - if (errret == 0) - { + if (errret == 0) { debug(0, _(L"Check that your terminal type, '%ls', is supported on this system"), term.c_str()); debug(0, _(L"Attempting to use '%ls' instead"), DEFAULT_TERM); env_set(L"TERM", DEFAULT_TERM, ENV_GLOBAL | ENV_EXPORT); const std::string default_term = wcs2string(DEFAULT_TERM); - if (setupterm(const_cast(default_term.c_str()), STDOUT_FILENO, &errret) == ERR) - { + if (setupterm(const_cast(default_term.c_str()), STDOUT_FILENO, &errret) == + ERR) { debug(0, _(L"Could not set up terminal")); exit_without_destructors(1); } - } - else - { + } else { exit_without_destructors(1); } } - assert(! term.missing()); + assert(!term.missing()); output_set_term(term); input_terminfo_init(); update_fish_color_support(); - /* If we have no keybindings, add a few simple defaults */ - if (mapping_list.empty()) - { + // 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"\r", L"execute"); @@ -508,340 +417,260 @@ int input_init() return 1; } -void input_destroy() -{ - if (!is_init) - return; - - +void input_destroy() { + if (!is_init) return; is_init = false; - input_common_destroy(); - if (del_curterm(cur_term) == ERR) - { + if (del_curterm(cur_term) == ERR) { debug(0, _(L"Error while closing terminfo")); } } -void input_function_push_arg(wchar_t arg) -{ +void input_function_push_arg(wchar_t arg) { input_function_args[input_function_args_index++] = arg; } -wchar_t input_function_pop_arg() -{ - return input_function_args[--input_function_args_index]; -} +wchar_t input_function_pop_arg() { return input_function_args[--input_function_args_index]; } -void input_function_push_args(int code) -{ +void input_function_push_args(int code) { int arity = input_function_arity(code); std::vector skipped; - for (int i = 0; i < arity; i++) - { + for (int i = 0; i < arity; i++) { wchar_t arg; - // skip and queue up any function codes - // See #2357 - while(((arg = input_common_readch(0)) >= R_MIN) && (arg <= R_MAX)) - { + // Skip and queue up any function codes. See issue #2357. + while (((arg = input_common_readch(0)) >= R_MIN) && (arg <= R_MAX)) { skipped.push_back(arg); } input_function_push_arg(arg); } - // push the function codes back into the input stream + // Push the function codes back into the input stream. size_t idx = skipped.size(); - while (idx--) - { + while (idx--) { input_common_next_ch(skipped.at(idx)); } } -/** - Perform the action of the specified binding - allow_commands controls whether fish commands should be executed, or should - be deferred until later. -*/ -static void input_mapping_execute(const input_mapping_t &m, bool allow_commands) -{ - /* has_functions: there are functions that need to be put on the input - queue - has_commands: there are shell commands that need to be evaluated */ +/// Perform the action of the specified binding. allow_commands controls whether fish commands +/// should be executed, or should be deferred until later. +static void input_mapping_execute(const input_mapping_t &m, bool allow_commands) { + // has_functions: there are functions that need to be put on the input queue + // has_commands: there are shell commands that need to be evaluated bool has_commands = false, has_functions = false; - for (wcstring_list_t::const_iterator it = m.commands.begin(), end = m.commands.end(); it != end; ++it) - { + for (wcstring_list_t::const_iterator it = m.commands.begin(), end = m.commands.end(); it != end; + ++it) { if (input_function_get_code(*it) != INPUT_CODE_NONE) has_functions = true; else has_commands = true; } - /* !has_functions && !has_commands: only set bind mode */ - if (!has_commands && !has_functions) - { + // !has_functions && !has_commands: only set bind mode + if (!has_commands && !has_functions) { input_set_bind_mode(m.sets_mode); return; } - if (has_commands && !allow_commands) - { - /* We don't want to run commands yet. Put the characters back and return - R_NULL */ - for (wcstring::const_reverse_iterator it = m.seq.rbegin(), end = m.seq.rend(); it != end; ++it) - { + if (has_commands && !allow_commands) { + // We don't want to run commands yet. Put the characters back and return R_NULL. + for (wcstring::const_reverse_iterator it = m.seq.rbegin(), end = m.seq.rend(); it != end; + ++it) { input_common_next_ch(*it); } input_common_next_ch(R_NULL); - return; /* skip the input_set_bind_mode */ - } - else if (has_functions && !has_commands) - { - /* functions are added at the head of the input queue */ - for (wcstring_list_t::const_reverse_iterator it = m.commands.rbegin(), end = m.commands.rend(); it != end; ++it) - { + return; // skip the input_set_bind_mode + } else if (has_functions && !has_commands) { + // Functions are added at the head of the input queue. + for (wcstring_list_t::const_reverse_iterator it = m.commands.rbegin(), + end = m.commands.rend(); + it != end; ++it) { wchar_t code = input_function_get_code(*it); input_function_push_args(code); input_common_next_ch(code); } - } - else if (has_commands && !has_functions) - { - /* Execute all commands. - - FIXME(snnw): if commands add stuff to input queue (e.g. commandline - -f execute), we won't see that until all other commands have also - been run. */ + } else if (has_commands && !has_functions) { + // Execute all commands. + // + // FIXME(snnw): if commands add stuff to input queue (e.g. commandline -f execute), we won't + // see that until all other commands have also been run. int last_status = proc_get_last_status(); - for (wcstring_list_t::const_iterator it = m.commands.begin(), end = m.commands.end(); it != end; ++it) - { + for (wcstring_list_t::const_iterator it = m.commands.begin(), end = m.commands.end(); + it != end; ++it) { parser_t::principal_parser().eval(it->c_str(), io_chain_t(), TOP); } proc_set_last_status(last_status); input_common_next_ch(R_NULL); - } - else - { - /* invalid binding, mixed commands and functions. We would need to - execute these one by one. */ + } else { + // Invalid binding, mixed commands and functions. We would need to execute these one by + // one. input_common_next_ch(R_NULL); } input_set_bind_mode(m.sets_mode); } - - -/** - Try reading the specified function mapping -*/ -static bool input_mapping_is_match(const input_mapping_t &m) -{ +/// Try reading the specified function mapping. +static bool input_mapping_is_match(const input_mapping_t &m) { wint_t c = 0; int j; - //debug(0, L"trying mapping %ls\n", escape(m.seq.c_str(), ESCAPE_ALL).c_str()); + // debug(0, L"trying mapping %ls\n", escape(m.seq.c_str(), ESCAPE_ALL).c_str()); const wchar_t *str = m.seq.c_str(); - for (j=0; str[j] != L'\0'; j++) - { + for (j = 0; str[j] != L'\0'; j++) { bool timed = (j > 0 && iswcntrl(str[0])); c = input_common_readch(timed); - if (str[j] != c) - { + if (str[j] != c) { break; } } - if (str[j] == L'\0') - { - //debug(0, L"matched mapping %ls (%ls)\n", escape(m.seq.c_str(), ESCAPE_ALL).c_str(), m.command.c_str()); - /* We matched the entire sequence */ + if (str[j] == L'\0') { + // debug(0, L"matched mapping %ls (%ls)\n", escape(m.seq.c_str(), ESCAPE_ALL).c_str(), + // m.command.c_str()); + // We matched the entire sequence. return true; - } - else - { + } else { int k; - /* - Return the read characters - */ + // Return the read characters. input_common_next_ch(c); - for (k=j-1; k>=0; k--) - { + for (k = j - 1; k >= 0; k--) { input_common_next_ch(m.seq[k]); } } return false; - } -void input_queue_ch(wint_t ch) -{ - input_common_queue_ch(ch); -} +void input_queue_ch(wint_t ch) { input_common_queue_ch(ch); } -static void input_mapping_execute_matching_or_generic(bool allow_commands) -{ +static void input_mapping_execute_matching_or_generic(bool allow_commands) { const input_mapping_t *generic = NULL; const wcstring bind_mode = input_get_bind_mode(); - for (int i = 0; i < mapping_list.size(); i++) - { + for (int i = 0; i < mapping_list.size(); i++) { const input_mapping_t &m = mapping_list.at(i); - //debug(0, L"trying mapping (%ls,%ls,%ls)\n", escape(m.seq.c_str(), ESCAPE_ALL).c_str(), + // debug(0, L"trying mapping (%ls,%ls,%ls)\n", escape(m.seq.c_str(), ESCAPE_ALL).c_str(), // m.mode.c_str(), m.sets_mode.c_str()); - if (m.mode != bind_mode) - { - //debug(0, L"skipping mapping because mode %ls != %ls\n", m.mode.c_str(), input_get_bind_mode().c_str()); + if (m.mode != bind_mode) { + // debug(0, L"skipping mapping because mode %ls != %ls\n", m.mode.c_str(), + // input_get_bind_mode().c_str()); continue; } - if (m.seq.length() == 0) - { + if (m.seq.length() == 0) { generic = &m; - } - else if (input_mapping_is_match(m)) - { + } else if (input_mapping_is_match(m)) { input_mapping_execute(m, allow_commands); return; } } - if (generic) - { + if (generic) { input_mapping_execute(*generic, allow_commands); - } - else - { - //debug(0, L"no generic found, ignoring..."); + } else { + // debug(0, L"no generic found, ignoring..."); wchar_t c = input_common_readch(0); - if (c == R_EOF) - input_common_next_ch(c); + if (c == R_EOF) input_common_next_ch(c); } } -/* Helper function. Picks through the queue of incoming characters until we get to one that's not a readline function. */ -static wchar_t input_read_characters_only() -{ +/// Helper function. Picks through the queue of incoming characters until we get to one that's not a +/// readline function. +static wchar_t input_read_characters_only() { std::vector functions_to_put_back; wchar_t char_to_return; - for (;;) - { + for (;;) { char_to_return = input_common_readch(0); bool is_readline_function = (char_to_return >= R_MIN && char_to_return <= R_MAX); - // R_NULL and R_EOF are more control characters than readline functions, so check specially for those - if (!is_readline_function || char_to_return == R_NULL || char_to_return == R_EOF) - { + // R_NULL and R_EOF are more control characters than readline functions, so check specially + // for those. + if (!is_readline_function || char_to_return == R_NULL || char_to_return == R_EOF) { break; } - // This is a readline function; save it off for later re-enqueing and try again + // This is a readline function; save it off for later re-enqueing and try again. functions_to_put_back.push_back(char_to_return); } - // Restore any readline functions, in reverse to preserve their original order + // Restore any readline functions, in reverse to preserve their original order. size_t idx = functions_to_put_back.size(); - while (idx--) - { + while (idx--) { input_common_next_ch(functions_to_put_back.at(idx)); } return char_to_return; } -wint_t input_readch(bool allow_commands) -{ +wint_t input_readch(bool allow_commands) { CHECK_BLOCK(R_NULL); - /* - Clear the interrupted flag - */ + // Clear the interrupted flag. reader_reset_interrupted(); - - /* - Search for sequence in mapping tables - */ - - while (1) - { + // Search for sequence in mapping tables. + while (1) { wchar_t c = input_common_readch(0); - if (c >= R_MIN && c <= R_MAX) - { - switch (c) - { - case R_EOF: /* If it's closed, then just return */ + if (c >= R_MIN && c <= R_MAX) { + switch (c) { + case R_EOF: // if it's closed, then just return { return R_EOF; } - case R_SELF_INSERT: - { - /* #1595: ensure we only insert characters, not readline functions. The common case is that this will be empty. */ + case R_SELF_INSERT: { + // Issue #1595: ensure we only insert characters, not readline functions. The + // common case is that this will be empty. return input_read_characters_only(); } - case R_AND: - { - if (input_function_status) - { + case R_AND: { + if (input_function_status) { return input_readch(); - } - else - { - while ((c = input_common_readch(0)) && c >= R_MIN && c <= R_MAX); + } else { + while ((c = input_common_readch(0)) && c >= R_MIN && c <= R_MAX) + ; input_common_next_ch(c); return input_readch(); } } - default: - { - return c; - } + default: { return c; } } - } - else - { + } else { input_common_next_ch(c); input_mapping_execute_matching_or_generic(allow_commands); - // regarding allow_commands, we're in a loop, but if a fish command + // Regarding allow_commands, we're in a loop, but if a fish command // is executed, R_NULL is unread, so the next pass through the loop // we'll break out and return it. } } } -std::vector input_mapping_get_names() -{ - // Sort the mappings by the user specification order, so we can return them in the same order that the user specified them in +std::vector input_mapping_get_names() { + // Sort the mappings by the user specification order, so we can return them in the same order + // that the user specified them in. std::vector local_list = mapping_list; std::sort(local_list.begin(), local_list.end(), specification_order_is_less_than); std::vector result; result.reserve(local_list.size()); - for (size_t i=0; i::iterator it = mapping_list.begin(), end = mapping_list.end(); - it != end; - ++it) - { - if (sequence == it->seq && mode == it->mode) - { + it != end; ++it) { + if (sequence == it->seq && mode == it->mode) { mapping_list.erase(it); result = true; break; @@ -850,15 +679,13 @@ bool input_mapping_erase(const wcstring &sequence, const wcstring &mode) return result; } -bool input_mapping_get(const wcstring &sequence, const wcstring &mode, wcstring_list_t *out_cmds, wcstring *out_sets_mode) -{ +bool input_mapping_get(const wcstring &sequence, const wcstring &mode, wcstring_list_t *out_cmds, + wcstring *out_sets_mode) { bool result = false; - for (std::vector::const_iterator it = mapping_list.begin(), end = mapping_list.end(); - it != end; - ++it) - { - if (sequence == it->seq && mode == it->mode) - { + for (std::vector::const_iterator it = mapping_list.begin(), + end = mapping_list.end(); + it != end; ++it) { + if (sequence == it->seq && mode == it->mode) { *out_cmds = it->commands; *out_sets_mode = it->sets_mode; result = true; @@ -868,13 +695,9 @@ bool input_mapping_get(const wcstring &sequence, const wcstring &mode, wcstring_ return result; } -/** - Add all terminfo mappings - */ -static void input_terminfo_init() -{ - const terminfo_mapping_t tinfos[] = - { +/// Add all terminfo mappings. +static void input_terminfo_init() { + const terminfo_mapping_t tinfos[] = { TERMINFO_ADD(key_a1), TERMINFO_ADD(key_a3), TERMINFO_ADD(key_b2), @@ -921,13 +744,10 @@ static void input_terminfo_init() TERMINFO_ADD(key_f18), TERMINFO_ADD(key_f19), TERMINFO_ADD(key_f20), - /* - I know of no keyboard with more than 20 function keys, so - adding the rest here makes very little sense, since it will - take up a lot of room in any listings (like tab completions), - but with no benefit. - */ - /* +#if 0 + // I know of no keyboard with more than 20 function keys, so adding the rest here makes very + // little sense, since it will take up a lot of room in any listings (like tab completions), + // but with no benefit. TERMINFO_ADD(key_f21), TERMINFO_ADD(key_f22), TERMINFO_ADD(key_f23), @@ -970,7 +790,8 @@ static void input_terminfo_init() TERMINFO_ADD(key_f60), TERMINFO_ADD(key_f61), TERMINFO_ADD(key_f62), - TERMINFO_ADD(key_f63),*/ + TERMINFO_ADD(key_f63), +#endif TERMINFO_ADD(key_find), TERMINFO_ADD(key_help), TERMINFO_ADD(key_home), @@ -1037,8 +858,7 @@ static void input_terminfo_init() terminfo_mappings.insert(terminfo_mappings.end(), tinfos, tinfos + count); } -bool input_terminfo_get_sequence(const wchar_t *name, wcstring *out_seq) -{ +bool input_terminfo_get_sequence(const wchar_t *name, wcstring *out_seq) { ASSERT_IS_MAIN_THREAD(); const char *res = 0; @@ -1047,44 +867,36 @@ bool input_terminfo_get_sequence(const wchar_t *name, wcstring *out_seq) CHECK(name, 0); input_init(); - for (size_t i=0; iassign(m.name); return true; } @@ -1093,19 +905,16 @@ bool input_terminfo_get_name(const wcstring &seq, wcstring *out_name) return false; } -wcstring_list_t input_terminfo_get_names(bool skip_null) -{ +wcstring_list_t input_terminfo_get_names(bool skip_null) { wcstring_list_t result; result.reserve(terminfo_mappings.size()); input_init(); - for (size_t i=0; i #include #include -#include #include "common.h" #include "env.h" @@ -18,121 +15,93 @@ inputrc information for key bindings. wcstring describe_char(wint_t c); -/** - Initialize the terminal by calling setupterm, and set up arrays - used by readch to detect escape sequences for special keys. - - Before calling input_init, terminfo is not initialized and MUST not be used -*/ +/// Initialize the terminal by calling setupterm, and set up arrays used by readch to detect escape +/// sequences for special keys. +/// +/// Before calling input_init, terminfo is not initialized and MUST not be used. int input_init(); -/** - free up memory used by terminal functions. -*/ +/// free up memory used by terminal functions. void input_destroy(); -/** - Read a character from fd 0. Try to convert some escape sequences - into character constants, but do not permanently block the escape - character. - - This is performed in the same way vim does it, i.e. if an escape - character is read, wait for more input for a short time (a few - milliseconds). If more input is avaialable, it is assumed to be an - escape sequence for a special character (such as an arrow key), and - readch attempts to parse it. If no more input follows after the - escape key, it is assumed to be an actual escape key press, and is - returned as such. - - The argument determines whether fish commands are allowed to be run - as bindings. If false, when a character is encountered that would - invoke a fish command, it is unread and R_NULL is returned. -*/ +/// Read a character from fd 0. Try to convert some escape sequences into character constants, but +/// do not permanently block the escape character. +/// +/// This is performed in the same way vim does it, i.e. if an escape character is read, wait for +/// more input for a short time (a few milliseconds). If more input is avaialable, it is assumed to +/// be an escape sequence for a special character (such as an arrow key), and readch attempts to +/// parse it. If no more input follows after the escape key, it is assumed to be an actual escape +/// key press, and is returned as such. +/// +/// The argument determines whether fish commands are allowed to be run as bindings. If false, when +/// a character is encountered that would invoke a fish command, it is unread and R_NULL is +/// returned. wint_t input_readch(bool allow_commands = true); -/** - Enqueue a character or a readline function to the queue of unread - characters that input_readch will return before actually reading from fd - 0. - */ +/// Enqueue a character or a readline function to the queue of unread characters that input_readch +/// will return before actually reading from fd 0. void input_queue_ch(wint_t ch); - -/** - Add a key mapping from the specified sequence to the specified command - - \param sequence the sequence to bind - \param command an input function that will be run whenever the key sequence occurs -*/ +/// Add a key mapping from the specified sequence to the specified command. +/// +/// \param sequence the sequence to bind +/// \param command an input function that will be run whenever the key sequence occurs void input_mapping_add(const wchar_t *sequence, const wchar_t *command, const wchar_t *mode = DEFAULT_BIND_MODE, const wchar_t *new_mode = DEFAULT_BIND_MODE); -void input_mapping_add(const wchar_t *sequence, const wchar_t * const *commands, size_t commands_len, - const wchar_t *mode = DEFAULT_BIND_MODE, const wchar_t *new_mode = DEFAULT_BIND_MODE); +void input_mapping_add(const wchar_t *sequence, const wchar_t *const *commands, size_t commands_len, + const wchar_t *mode = DEFAULT_BIND_MODE, + const wchar_t *new_mode = DEFAULT_BIND_MODE); struct input_mapping_name_t { wcstring seq; wcstring mode; }; -/** - Returns all mapping names and modes - */ +/// Returns all mapping names and modes. std::vector input_mapping_get_names(); -/** - Erase binding for specified key sequence - */ +/// Erase binding for specified key sequence. bool input_mapping_erase(const wcstring &sequence, const wcstring &mode = DEFAULT_BIND_MODE); -/** - Gets the command bound to the specified key sequence in the specified mode. Returns true if it exists, false if not. - */ -bool input_mapping_get(const wcstring &sequence, const wcstring &mode, wcstring_list_t *out_cmds, wcstring *out_new_mode); +/// Gets the command bound to the specified key sequence in the specified mode. Returns true if it +/// exists, false if not. +bool input_mapping_get(const wcstring &sequence, const wcstring &mode, wcstring_list_t *out_cmds, + wcstring *out_new_mode); -/** - Return the current bind mode -*/ +/// Return the current bind mode. wcstring input_get_bind_mode(); -/** - Set the current bind mode -*/ +/// Set the current bind mode. void input_set_bind_mode(const wcstring &bind_mode); - wchar_t input_function_pop_arg(); - -/** - Sets the return status of the most recently executed input function -*/ +/// Sets the return status of the most recently executed input function. void input_function_set_status(bool status); -/** - Return the sequence for the terminfo variable of the specified name. - - If no terminfo variable of the specified name could be found, return false and set errno to ENOENT. - If the terminfo variable does not have a value, return false and set errno to EILSEQ. - */ +/// Return the sequence for the terminfo variable of the specified name. +/// +/// If no terminfo variable of the specified name could be found, return false and set errno to +/// ENOENT. If the terminfo variable does not have a value, return false and set errno to EILSEQ. bool input_terminfo_get_sequence(const wchar_t *name, wcstring *out_seq); -/** Return the name of the terminfo variable with the specified sequence, in out_name. Returns true if found, false if not found. */ +/// Return the name of the terminfo variable with the specified sequence, in out_name. Returns true +/// if found, false if not found. bool input_terminfo_get_name(const wcstring &seq, wcstring *out_name); -/** Return a list of all known terminfo names */ +/// Return a list of all known terminfo names. wcstring_list_t input_terminfo_get_names(bool skip_null); -/** Returns the input function code for the given input function name. */ +/// Returns the input function code for the given input function name. #define INPUT_CODE_NONE (wchar_t(-1)) wchar_t input_function_get_code(const wcstring &name); -/** Returns a list of all existing input function names */ +/// Returns a list of all existing input function names. wcstring_list_t input_function_get_names(void); -/** Updates our idea of whether we support term256 and term24bit */ +/// Updates our idea of whether we support term256 and term24bit. void update_fish_color_support(); - #endif From 08c29727e01a541f54f9161e08cbb10020d9d456 Mon Sep 17 00:00:00 2001 From: Jorge Bucaran Date: Sun, 1 May 2016 18:58:43 +0900 Subject: [PATCH 189/363] Add missing color definitions to __fish_init_1_50_0 reset. (#2987) * Add missing color definitions to __fish_init_1_50_0 reset. The values where determined by inspecting the values of: * fish_color_end * fish_color_user * fish_color_host after resetting the color theme via fish_config. * Add documentation for fish_color_user and fish_color_host. --- doc_src/index.hdr.in | 4 ++++ share/functions/__fish_config_interactive.fish | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index c06d39d89..24f70e318 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -900,6 +900,10 @@ The following variables are available to change the highlighting colors in fish: - `fish_color_autosuggestion`, the color used for autosuggestions +- `fish_color_user`, the color used to print the current username in some of fish default prompts + +- `fish_color_host`, the color used to print the current host system in some of fish default prompts + Additionally, the following variables are available to change the highlighting in the completion pager: - `fish_pager_color_prefix`, the color of the prefix string, i.e. the string that is to be completed diff --git a/share/functions/__fish_config_interactive.fish b/share/functions/__fish_config_interactive.fish index bca410ac5..f8b08de62 100644 --- a/share/functions/__fish_config_interactive.fish +++ b/share/functions/__fish_config_interactive.fish @@ -46,8 +46,12 @@ function __fish_config_interactive -d "Initializations that should be performed set -q fish_color_error; or set -U fish_color_error red --bold set -q fish_color_escape; or set -U fish_color_escape cyan set -q fish_color_operator; or set -U fish_color_operator cyan + set -q fish_color_end; or set -U fish_color_end green set -q fish_color_quote; or set -U fish_color_quote brown set -q fish_color_autosuggestion; or set -U fish_color_autosuggestion 555 yellow + set -q fish_color_user; or set -U fish_color_user green + + set -q fish_color_host; or set -U fish_color_host normal set -q fish_color_valid_path; or set -U fish_color_valid_path --underline set -q fish_color_cwd; or set -U fish_color_cwd green From da17420cdff419a9cd3f669fa72613908c15c866 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sun, 1 May 2016 19:54:25 -0700 Subject: [PATCH 190/363] restyle input_common module to match project style Reduces lint errors from 27 to 24 (-11%). Line count from 466 to 378 (-19%). Another step in resolving issue #2902. --- src/input_common.cpp | 270 ++++++++++++++++--------------------------- src/input_common.h | 66 ++++------- 2 files changed, 124 insertions(+), 212 deletions(-) diff --git a/src/input_common.cpp b/src/input_common.cpp index 3584f903d..93e94fd3f 100644 --- a/src/input_common.cpp +++ b/src/input_common.cpp @@ -1,35 +1,32 @@ -/** \file input_common.c - -Implementation file for the low level input library -*/ +// Implementation file for the low level input library. #include "config.h" -#include #include +#include #include +#include #include #include -#include #include #ifdef HAVE_SYS_SELECT_H #include #endif #include -#include -#include -#include -#include -#include #include #include +#include +#include +#include +#include +#include -#include "fallback.h" // IWYU pragma: keep -#include "util.h" #include "common.h" -#include "input_common.h" -#include "env_universal_common.h" #include "env.h" +#include "env_universal_common.h" +#include "fallback.h" // IWYU pragma: keep +#include "input_common.h" #include "iothread.h" +#include "util.h" // Time in milliseconds to wait for another byte to be available for reading // after \x1b is read before assuming that escape key was pressed, and not an @@ -37,69 +34,49 @@ Implementation file for the low level input library #define WAIT_ON_ESCAPE_DEFAULT 300 static int wait_on_escape_ms = WAIT_ON_ESCAPE_DEFAULT; -/** Characters that have been read and returned by the sequence matching code */ +/// Characters that have been read and returned by the sequence matching code. static std::deque lookahead_list; -/* Queue of pairs of (function pointer, argument) to be invoked. Expected to be mostly empty. */ +// Queue of pairs of (function pointer, argument) to be invoked. Expected to be mostly empty. typedef std::pair callback_info_t; typedef std::queue > callback_queue_t; static callback_queue_t callback_queue; static void input_flush_callbacks(void); -static bool has_lookahead(void) -{ - return ! lookahead_list.empty(); -} +static bool has_lookahead(void) { return !lookahead_list.empty(); } -static wint_t lookahead_pop(void) -{ +static wint_t lookahead_pop(void) { wint_t result = lookahead_list.front(); lookahead_list.pop_front(); return result; } -static void lookahead_push_back(wint_t c) -{ - lookahead_list.push_back(c); -} +static void lookahead_push_back(wint_t c) { lookahead_list.push_back(c); } -static void lookahead_push_front(wint_t c) -{ - lookahead_list.push_front(c); -} +static void lookahead_push_front(wint_t c) { lookahead_list.push_front(c); } -static wint_t lookahead_front(void) -{ - return lookahead_list.front(); -} +static wint_t lookahead_front(void) { return lookahead_list.front(); } -/** Callback function for handling interrupts on reading */ +/// Callback function for handling interrupts on reading. static int (*interrupt_handler)(); -void input_common_init(int (*ih)()) -{ +void input_common_init(int (*ih)()) { interrupt_handler = ih; update_wait_on_escape_ms(); } -void input_common_destroy() -{ +void input_common_destroy() {} -} - -/** - Internal function used by input_common_readch to read one byte from fd 0. This function should only be called by - input_common_readch(). -*/ -static wint_t readb() -{ - /* do_loop must be set on every path through the loop; leaving it uninitialized allows the static analyzer to assist in catching mistakes. */ +/// Internal function used by input_common_readch to read one byte from fd 0. This function should +/// only be called by input_common_readch(). +static wint_t readb() { + // do_loop must be set on every path through the loop; leaving it uninitialized allows the + // static analyzer to assist in catching mistakes. unsigned char arr[1]; bool do_loop; - do - { - /* Flush callbacks */ + do { + // Flush callbacks. input_flush_callbacks(); fd_set fdset; @@ -109,119 +86,94 @@ static wint_t readb() FD_ZERO(&fdset); FD_SET(0, &fdset); - if (ioport > 0) - { + if (ioport > 0) { FD_SET(ioport, &fdset); fd_max = maxi(fd_max, ioport); } - - /* Get our uvar notifier */ + + // Get our uvar notifier. universal_notifier_t ¬ifier = universal_notifier_t::default_notifier(); - - /* Get the notification fd (possibly none) */ + + // Get the notification fd (possibly none). int notifier_fd = notifier.notification_fd(); - if (notifier_fd > 0) - { + if (notifier_fd > 0) { FD_SET(notifier_fd, &fdset); fd_max = maxi(fd_max, notifier_fd); } - - /* Get its suggested delay (possibly none) */ + + // Get its suggested delay (possibly none). struct timeval tv = {}; const unsigned long usecs_delay = notifier.usec_delay_between_polls(); - if (usecs_delay > 0) - { + if (usecs_delay > 0) { unsigned long usecs_per_sec = 1000000; tv.tv_sec = (int)(usecs_delay / usecs_per_sec); tv.tv_usec = (int)(usecs_delay % usecs_per_sec); } - + res = select(fd_max + 1, &fdset, 0, 0, usecs_delay > 0 ? &tv : NULL); - if (res==-1) - { - switch (errno) - { + if (res == -1) { + switch (errno) { case EINTR: - case EAGAIN: - { - if (interrupt_handler) - { + case EAGAIN: { + if (interrupt_handler) { int res = interrupt_handler(); - if (res) - { + if (res) { return res; } - if (has_lookahead()) - { + if (has_lookahead()) { return lookahead_pop(); } - } - do_loop = true; break; } - default: - { - /* - The terminal has been closed. Save and exit. - */ + default: { + // The terminal has been closed. Save and exit. return R_EOF; } } - } - else - { - /* Assume we loop unless we see a character in stdin */ + } else { + // Assume we loop unless we see a character in stdin. do_loop = true; - /* Check to see if we want a universal variable barrier */ + // Check to see if we want a universal variable barrier. bool barrier_from_poll = notifier.poll(); bool barrier_from_readability = false; - if (notifier_fd > 0 && FD_ISSET(notifier_fd, &fdset)) - { + if (notifier_fd > 0 && FD_ISSET(notifier_fd, &fdset)) { barrier_from_readability = notifier.notification_fd_became_readable(notifier_fd); } - if (barrier_from_poll || barrier_from_readability) - { + if (barrier_from_poll || barrier_from_readability) { env_universal_barrier(); } - if (ioport > 0 && FD_ISSET(ioport, &fdset)) - { + if (ioport > 0 && FD_ISSET(ioport, &fdset)) { iothread_service_completion(); - if (has_lookahead()) - { + if (has_lookahead()) { return lookahead_pop(); } } - if (FD_ISSET(STDIN_FILENO, &fdset)) - { - if (read_blocked(0, arr, 1) != 1) - { - /* The teminal has been closed. Save and exit. */ + 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 */ + // We read from stdin, so don't loop. do_loop = false; } } - } - while (do_loop); + } while (do_loop); return arr[0]; } -// Update the wait_on_escape_ms value in response to the fish_escape_delay_ms -// user variable being set. -void update_wait_on_escape_ms() -{ +// Update the wait_on_escape_ms value in response to the fish_escape_delay_ms user variable being +// set. +void update_wait_on_escape_ms() { env_var_t escape_time_ms = env_get_string(L"fish_escape_delay_ms"); - if (escape_time_ms.missing_or_empty()) - { + if (escape_time_ms.missing_or_empty()) { wait_on_escape_ms = WAIT_ON_ESCAPE_DEFAULT; return; } @@ -229,33 +181,25 @@ void update_wait_on_escape_ms() wchar_t *endptr; long tmp = wcstol(escape_time_ms.c_str(), &endptr, 10); - if (*endptr != '\0' || tmp < 10 || tmp >= 5000) - { - fwprintf(stderr, L"ignoring fish_escape_delay_ms: value '%ls' " - "is not an integer or is < 10 or >= 5000 ms\n", - escape_time_ms.c_str()); - } - else - { + if (*endptr != '\0' || tmp < 10 || tmp >= 5000) { + fwprintf(stderr, + L"ignoring fish_escape_delay_ms: value '%ls' " + "is not an integer or is < 10 or >= 5000 ms\n", + escape_time_ms.c_str()); + } else { wait_on_escape_ms = (int)tmp; } } - -wchar_t input_common_readch(int timed) -{ - if (! has_lookahead()) - { - if (timed) - { +wchar_t input_common_readch(int timed) { + if (!has_lookahead()) { + if (timed) { fd_set fds; FD_ZERO(&fds); FD_SET(0, &fds); - struct timeval tm = {wait_on_escape_ms / 1000, - 1000 * (wait_on_escape_ms % 1000)}; + struct timeval tm = {wait_on_escape_ms / 1000, 1000 * (wait_on_escape_ms % 1000)}; int count = select(1, &fds, 0, 0, &tm); - if (count <= 0) - { + if (count <= 0) { return WEOF; } } @@ -263,80 +207,64 @@ wchar_t input_common_readch(int timed) wchar_t res; mbstate_t state = {}; - while (1) - { + while (1) { wint_t b = readb(); - if (MB_CUR_MAX == 1) // single-byte locale, all values are legal + if (MB_CUR_MAX == 1) // single-byte locale, all values are legal { return (unsigned char)b; } - if ((b >= R_NULL) && (b < R_NULL + 1000)) - return b; + if ((b >= R_NULL) && (b < R_NULL + 1000)) return b; char bb = b; size_t sz = mbrtowc(&res, &bb, 1, &state); - switch (sz) - { - case (size_t)(-1): + switch (sz) { + case (size_t)(-1): { memset(&state, '\0', sizeof(state)); debug(2, L"Illegal input"); return R_NULL; - case (size_t)(-2): + } + case (size_t)(-2): { break; - case 0: + } + case 0: { return 0; - default: - return res; + } + default: { return res; } } } - } - else - { - if (!timed) - { - while (has_lookahead() && lookahead_front() == WEOF) - lookahead_pop(); - if (! has_lookahead()) - return input_common_readch(0); + } else { + if (!timed) { + while (has_lookahead() && lookahead_front() == WEOF) lookahead_pop(); + if (!has_lookahead()) return input_common_readch(0); } return lookahead_pop(); } } +void input_common_queue_ch(wint_t ch) { lookahead_push_back(ch); } -void input_common_queue_ch(wint_t ch) -{ - lookahead_push_back(ch); -} +void input_common_next_ch(wint_t ch) { lookahead_push_front(ch); } -void input_common_next_ch(wint_t ch) -{ - lookahead_push_front(ch); -} - -void input_common_add_callback(void (*callback)(void *), void *arg) -{ +void input_common_add_callback(void (*callback)(void *), void *arg) { ASSERT_IS_MAIN_THREAD(); callback_queue.push(callback_info_t(callback, arg)); } -static void input_flush_callbacks(void) -{ - /* Nothing to do if nothing to do */ - if (callback_queue.empty()) - return; +static void input_flush_callbacks(void) { + // Nothing to do if nothing to do. + if (callback_queue.empty()) return; - /* We move the queue into a local variable, so that events queued up during a callback don't get fired until next round. */ + // We move the queue into a local variable, so that events queued up during a callback don't get + // fired until next round. callback_queue_t local_queue; std::swap(local_queue, callback_queue); - while (! local_queue.empty()) - { + while (!local_queue.empty()) { const callback_info_t &callback = local_queue.front(); - callback.first(callback.second); //cute + callback.first(callback.second); // cute local_queue.pop(); } } diff --git a/src/input_common.h b/src/input_common.h index 1f54317a4..d7bafe3fa 100644 --- a/src/input_common.h +++ b/src/input_common.h @@ -1,7 +1,4 @@ -/** \file input_common.h - -Header file for the low level input library -*/ +// Header file for the low level input library. #ifndef INPUT_COMMON_H #define INPUT_COMMON_H @@ -9,18 +6,17 @@ Header file for the low level input library #include "common.h" -enum -{ +enum { R_MIN = INPUT_COMMON_BASE, - // 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 is sometimes returned by the input when a character was requested but none could be + // delivered, or when an exception happened. R_NULL = R_MIN, R_EOF, - // Key codes for inputrc-style keyboard functions that are passed on - // to the caller of input_read(). + // Key codes for inputrc-style keyboard functions that are passed on to the caller of + // input_read(). // - // NOTE: If you modify this sequence of symbols you must update the - // name_arr, code_arr and desc_arr variables in input.cpp to match! + // NOTE: If you modify this sequence of symbols you must update the name_arr, code_arr and + // desc_arr variables in input.cpp to match! R_BEGINNING_OF_LINE, R_END_OF_LINE, R_FORWARD_CHAR, @@ -75,50 +71,38 @@ enum R_AND, R_CANCEL, R_MAX = R_CANCEL, - // This is a special psuedo-char that is not used other than to mark the - // end of the the special characters so we can sanity check the enum range. + // This is a special psuedo-char that is not used other than to mark the end of the the special + // characters so we can sanity check the enum range. R_SENTINAL }; -/** - Init the library -*/ +/// Init the library. void input_common_init(int (*ih)()); -/** - Free memory used by the library -*/ +/// Free memory used by the library. void input_common_destroy(); -// Adjust the escape timeout. +/// Adjust the escape timeout. void update_wait_on_escape_ms(); -/** - Function used by input_readch to read bytes from stdin until enough - bytes have been read to convert them to a wchar_t. Conversion is - done using mbrtowc. If a character has previously been read and - then 'unread' using \c input_common_unreadch, that character is - returned. If timed is true, readch2 will wait at most - WAIT_ON_ESCAPE milliseconds for a character to be available for - reading before returning with the value WEOF. -*/ +/// Function used by input_readch to read bytes from stdin until enough bytes have been read to +/// convert them to a wchar_t. Conversion is done using mbrtowc. If a character has previously been +/// read and then 'unread' using \c input_common_unreadch, that character is returned. If timed is +/// true, readch2 will wait at most 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); -/** - Enqueue a character or a readline function to the queue of unread - characters that input_readch will return before actually reading from fd - 0. -*/ +/// Enqueue a character or a readline function to the queue of unread characters that input_readch +/// will return before actually reading from fd 0. void input_common_queue_ch(wint_t ch); -/** - Add a character or a readline function to the front of the queue of unread - characters. This will be the first character returned by input_readch - (unless this function is called more than once). -*/ +/// Add a character or a readline function to the front of the queue of unread characters. This +/// will be the first character returned by input_readch (unless this function is called more than +/// once). void input_common_next_ch(wint_t ch); -/** Adds a callback to be invoked at the next turn of the "event loop." The callback function will be invoked and passed arg. */ +/// Adds a callback to be invoked at the next turn of the "event loop." The callback function will +/// be invoked and passed arg. void input_common_add_callback(void (*callback)(void *), void *arg); #endif From 8b2cf81f17f1da78c2fdc2940dd489a2321c247a Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sun, 1 May 2016 20:26:32 -0700 Subject: [PATCH 191/363] restyle intern module to match project style Reduces lint errors from 8 to 6 (-25%). Line count from 112 to 83 (-26%). Another step in resolving issue #2902. --- src/intern.cpp | 72 ++++++++++++++++++-------------------------------- src/intern.h | 27 +++++++------------ 2 files changed, 35 insertions(+), 64 deletions(-) diff --git a/src/intern.cpp b/src/intern.cpp index 0724fcd03..6171bc386 100644 --- a/src/intern.cpp +++ b/src/intern.cpp @@ -1,72 +1,59 @@ -/** \file intern.c - - Library for pooling common strings -*/ -#include +// Library for pooling common strings. #include -#include +#include +#include +#include #include #include -#include -#include +#include -#include "fallback.h" // IWYU pragma: keep #include "common.h" +#include "fallback.h" // IWYU pragma: keep #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 - { - return wcscmp(a, b) < 0; - } +/// Comparison function for intern'd strings. +class string_table_compare_t { + public: + bool operator()(const wchar_t *a, const wchar_t *b) const { return wcscmp(a, b) < 0; } }; -/* A sorted vector ends up being a little more memory efficient than a std::set for the intern'd string table */ +// A sorted vector ends up being a little more memory efficient than a std::set for the intern'd +// string table. #define USE_SET 0 #if USE_SET -/** The table of intern'd strings */ +/// The table of intern'd strings. typedef std::set string_table_t; #else -/** The table of intern'd strings */ +/// The table of intern'd strings. typedef std::vector string_table_t; #endif static string_table_t string_table; -/** The lock to provide thread safety for intern'd strings */ +/// 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) -{ - if (!in) - return NULL; +static const wchar_t *intern_with_dup(const wchar_t *in, bool dup) { + if (!in) return NULL; -// debug( 0, L"intern %ls", in ); + // debug( 0, L"intern %ls", in ); scoped_lock lock(intern_lock); const wchar_t *result; #if USE_SET string_table_t::const_iterator iter = string_table.find(in); - if (iter != string_table.end()) - { + 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) - { + 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) { result = *iter; - } - else - { + } else { result = dup ? wcsdup(in) : in; string_table.insert(iter, result); } @@ -74,13 +61,6 @@ static const wchar_t *intern_with_dup(const wchar_t *in, bool dup) return result; } -const wchar_t *intern(const wchar_t *in) -{ - return intern_with_dup(in, true); -} +const wchar_t *intern(const wchar_t *in) { return intern_with_dup(in, true); } - -const wchar_t *intern_static(const wchar_t *in) -{ - return intern_with_dup(in, false); -} +const wchar_t *intern_static(const wchar_t *in) { return intern_with_dup(in, false); } diff --git a/src/intern.h b/src/intern.h index b9d07526f..cba471451 100644 --- a/src/intern.h +++ b/src/intern.h @@ -1,26 +1,17 @@ -/** \file intern.h - - Library for pooling common strings - -*/ - +// Library for pooling common strings. #ifndef FISH_INTERN_H #define FISH_INTERN_H -/** - Return an identical copy of the specified string from a pool of unique strings. If the string was not in the pool, add a copy. - - \param in the string to return an interned copy of -*/ +/// Return an identical copy of the specified string from a pool of unique strings. If the string +/// was not in the pool, add a copy. +/// +/// \param in the string to return an interned copy of. const wchar_t *intern(const wchar_t *in); -/** - Insert the specified string literal into the pool of unique - strings. The string will not first be copied, and it will not be - free'd on exit. - - \param in the string to add to the interned pool -*/ +/// Insert the specified string literal into the pool of unique strings. The string will not first +/// be copied, and it will not be free'd on exit. +/// +/// \param in the string to add to the interned pool const wchar_t *intern_static(const wchar_t *in); #endif From b19bfc0dd3e9f5a0f8530ebccf207cef0824de7e Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sun, 1 May 2016 20:32:40 -0700 Subject: [PATCH 192/363] restyle io module to match project style Reduces lint errors from 15 to 10 (-33%). Line count from 637 to 489 (-23%). Another step in resolving issue #2902. --- src/io.cpp | 269 ++++++++++++++++++------------------------------ src/io.h | 295 ++++++++++++++++++++--------------------------------- 2 files changed, 208 insertions(+), 356 deletions(-) diff --git a/src/io.cpp b/src/io.cpp index ed5afe557..72bae3dba 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -1,181 +1,132 @@ -/** \file io.c - -Utilities for io redirection. -*/ -#include -#include +// Utilities for io redirection. #include -#include +#include #include +#include +#include -#include "fallback.h" // IWYU pragma: keep -#include "wutil.h" // IWYU pragma: keep -#include "exec.h" #include "common.h" +#include "exec.h" +#include "fallback.h" // IWYU pragma: keep #include "io.h" +#include "wutil.h" // IWYU pragma: keep -io_data_t::~io_data_t() -{ +io_data_t::~io_data_t() {} + +void io_close_t::print() const { fprintf(stderr, "close %d\n", fd); } + +void io_fd_t::print() const { fprintf(stderr, "FD map %d -> %d\n", old_fd, fd); } + +void io_file_t::print() const { fprintf(stderr, "file (%s)\n", filename_cstr); } + +void io_pipe_t::print() const { + fprintf(stderr, "pipe {%d, %d} (input: %s)\n", pipe_fd[0], pipe_fd[1], is_input ? "yes" : "no"); } -void io_close_t::print() const -{ - fprintf(stderr, "close %d\n", fd); +void io_buffer_t::print() const { + fprintf(stderr, "buffer %p (input: %s, size %lu)\n", out_buffer_ptr(), is_input ? "yes" : "no", + (unsigned long)out_buffer_size()); } -void io_fd_t::print() const -{ - fprintf(stderr, "FD map %d -> %d\n", old_fd, fd); -} - -void io_file_t::print() const -{ - fprintf(stderr, "file (%s)\n", filename_cstr); -} - -void io_pipe_t::print() const -{ - fprintf(stderr, "pipe {%d, %d} (input: %s)\n", pipe_fd[0], pipe_fd[1], - is_input ? "yes" : "no"); -} - -void io_buffer_t::print() const -{ - fprintf(stderr, "buffer %p (input: %s, size %lu)\n", out_buffer_ptr(), - is_input ? "yes" : "no", (unsigned long) out_buffer_size()); -} - -void io_buffer_t::read() -{ +void io_buffer_t::read() { exec_close(pipe_fd[1]); - if (io_mode == IO_BUFFER) - { - /* if( fcntl( pipe_fd[0], F_SETFL, 0 ) ) - { - wperror( L"fcntl" ); - return; - } */ + if (io_mode == IO_BUFFER) { +#if 0 + if (fcntl( pipe_fd[0], F_SETFL, 0)) { + wperror( L"fcntl" ); + return; + } +#endif debug(4, L"io_buffer_t::read: blocking read on fd %d", pipe_fd[0]); - while (1) - { + while (1) { char b[4096]; long l; - l=read_blocked(pipe_fd[0], b, 4096); - if (l==0) - { + l = read_blocked(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"), + } 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 " + L"descriptor %d"), pipe_fd[0]); wperror(L"io_buffer_t::read"); } break; - } - else - { + } else { out_buffer_append(b, l); } } } } -bool io_buffer_t::avoid_conflicts_with_io_chain(const io_chain_t &ios) -{ +bool io_buffer_t::avoid_conflicts_with_io_chain(const io_chain_t &ios) { bool result = pipe_avoid_conflicts_with_io_chain(this->pipe_fd, ios); - if (! result) - { + if (!result) { wperror(L"dup"); } return result; } -io_buffer_t *io_buffer_t::create(int fd, const io_chain_t &conflicts) -{ +io_buffer_t *io_buffer_t::create(int fd, const io_chain_t &conflicts) { bool success = true; assert(fd >= 0); io_buffer_t *buffer_redirect = new io_buffer_t(fd); - if (exec_pipe(buffer_redirect->pipe_fd) == -1) - { + if (exec_pipe(buffer_redirect->pipe_fd) == -1) { debug(1, PIPE_ERROR); wperror(L"pipe"); success = false; - } - else if (! buffer_redirect->avoid_conflicts_with_io_chain(conflicts)) - { - // The above call closes the fds on error + } else if (!buffer_redirect->avoid_conflicts_with_io_chain(conflicts)) { + // The above call closes the fds on error. success = false; - } - else if (make_fd_nonblocking(buffer_redirect->pipe_fd[0]) != 0) - { + } else if (make_fd_nonblocking(buffer_redirect->pipe_fd[0]) != 0) { debug(1, PIPE_ERROR); wperror(L"fcntl"); success = false; } - if (! success) - { + if (!success) { delete buffer_redirect; buffer_redirect = NULL; } return buffer_redirect; } -io_buffer_t::~io_buffer_t() -{ - if (pipe_fd[0] >= 0) - { +io_buffer_t::~io_buffer_t() { + if (pipe_fd[0] >= 0) { exec_close(pipe_fd[0]); } - - /* - Dont free fd for writing. This should already be free'd before - calling exec_read_io_buffer on the buffer - */ + // Dont free fd for writing. This should already be free'd before calling exec_read_io_buffer on + // the buffer. } -void io_chain_t::remove(const shared_ptr &element) -{ - // See if you can guess why std::find doesn't work here - for (io_chain_t::iterator iter = this->begin(); iter != this->end(); ++iter) - { - if (*iter == element) - { +void io_chain_t::remove(const shared_ptr &element) { + // See if you can guess why std::find doesn't work here. + for (io_chain_t::iterator iter = this->begin(); iter != this->end(); ++iter) { + if (*iter == element) { this->erase(iter); break; } } } -void io_chain_t::push_back(const shared_ptr &element) -{ - // Ensure we never push back NULL +void io_chain_t::push_back(const shared_ptr &element) { + // Ensure we never push back NULL. assert(element.get() != NULL); std::vector >::push_back(element); } -void io_chain_t::push_front(const shared_ptr &element) -{ +void io_chain_t::push_front(const shared_ptr &element) { assert(element.get() != NULL); this->insert(this->begin(), element); } -void io_chain_t::append(const io_chain_t &chain) -{ +void io_chain_t::append(const io_chain_t &chain) { this->insert(this->end(), chain.begin(), chain.end()); } @@ -207,33 +158,35 @@ void io_print(const io_chain_t &chain) } #endif -/* If the given fd is used by the io chain, duplicates it repeatedly until an fd not used in the io chain is found, or we run out. If we return a new fd or an error, closes the old one. Any fd created is marked close-on-exec. Returns -1 on failure (in which case the given fd is still closed). */ -static int move_fd_to_unused(int fd, const io_chain_t &io_chain) -{ +/// If the given fd is used by the io chain, duplicates it repeatedly until an fd not used in the io +/// chain is found, or we run out. If we return a new fd or an error, closes the old one. Any fd +/// created is marked close-on-exec. Returns -1 on failure (in which case the given fd is still +/// closed). +static int move_fd_to_unused(int fd, const io_chain_t &io_chain) { int new_fd = fd; - if (fd >= 0 && io_chain.get_io_for_fd(fd).get() != NULL) - { - /* We have fd >= 0, and it's a conflict. dup it and recurse. Note that we recurse before anything is closed; this forces the kernel to give us a new one (or report fd exhaustion). */ + if (fd >= 0 && io_chain.get_io_for_fd(fd).get() != NULL) { + // We have fd >= 0, and it's a conflict. dup it and recurse. Note that we recurse before + // anything is closed; this forces the kernel to give us a new one (or report fd + // exhaustion). int tmp_fd; - do - { + do { tmp_fd = dup(fd); } while (tmp_fd < 0 && errno == EINTR); - + assert(tmp_fd != fd); - if (tmp_fd < 0) - { - /* Likely fd exhaustion. */ + if (tmp_fd < 0) { + // Likely fd exhaustion. new_fd = -1; - } - else - { - /* Ok, we have a new candidate fd. Recurse. If we get a valid fd, either it's the same as what we gave it, or it's a new fd and what we gave it has been closed. If we get a negative value, the fd also has been closed. */ + } else { + // Ok, we have a new candidate fd. Recurse. If we get a valid fd, either it's the same + // as what we gave it, or it's a new fd and what we gave it has been closed. If we get a + // negative value, the fd also has been closed. set_cloexec(tmp_fd); new_fd = move_fd_to_unused(tmp_fd, io_chain); } - - /* We're either returning a new fd or an error. In both cases, we promise to close the old one. */ + + // We're either returning a new fd or an error. In both cases, we promise to close the old + // one. assert(new_fd != fd); int saved_errno = errno; exec_close(fd); @@ -242,27 +195,21 @@ static int move_fd_to_unused(int fd, const io_chain_t &io_chain) return new_fd; } -bool pipe_avoid_conflicts_with_io_chain(int fds[2], const io_chain_t &ios) -{ +bool pipe_avoid_conflicts_with_io_chain(int fds[2], const io_chain_t &ios) { bool success = true; - for (int i=0; i < 2; i++) - { + for (int i = 0; i < 2; i++) { fds[i] = move_fd_to_unused(fds[i], ios); - if (fds[i] < 0) - { + if (fds[i] < 0) { success = false; break; } } - - /* If any fd failed, close all valid fds */ - if (! success) - { + + // If any fd failed, close all valid fds. + if (!success) { int saved_errno = errno; - for (int i=0; i < 2; i++) - { - if (fds[i] >= 0) - { + for (int i = 0; i < 2; i++) { + if (fds[i] >= 0) { exec_close(fds[i]); fds[i] = -1; } @@ -272,53 +219,37 @@ bool pipe_avoid_conflicts_with_io_chain(int fds[2], const io_chain_t &ios) return success; } -/* Return the last IO for the given fd */ -shared_ptr io_chain_t::get_io_for_fd(int fd) const -{ +/// Return the last IO for the given fd. +shared_ptr io_chain_t::get_io_for_fd(int fd) const { size_t idx = this->size(); - while (idx--) - { + while (idx--) { const shared_ptr &data = this->at(idx); - if (data->fd == fd) - { + if (data->fd == fd) { return data; } } return shared_ptr(); } -shared_ptr io_chain_t::get_io_for_fd(int fd) -{ +shared_ptr io_chain_t::get_io_for_fd(int fd) { size_t idx = this->size(); - while (idx--) - { + while (idx--) { const shared_ptr &data = this->at(idx); - if (data->fd == fd) - { + if (data->fd == fd) { return data; } } return shared_ptr(); } -/* The old function returned the last match, so we mimic that. */ -shared_ptr io_chain_get(const io_chain_t &src, int fd) -{ +/// The old function returned the last match, so we mimic that. +shared_ptr io_chain_get(const io_chain_t &src, int fd) { return src.get_io_for_fd(fd); } -shared_ptr io_chain_get(io_chain_t &src, int fd) -{ - return src.get_io_for_fd(fd); -} +shared_ptr io_chain_get(io_chain_t &src, int fd) { return src.get_io_for_fd(fd); } -io_chain_t::io_chain_t(const shared_ptr &data) : - std::vector >(1, data) -{ - -} - -io_chain_t::io_chain_t() : std::vector >() -{ -} +io_chain_t::io_chain_t(const shared_ptr &data) + : std::vector >(1, data) {} +io_chain_t::io_chain_t() : std::vector >() {} diff --git a/src/io.h b/src/io.h index ed634f192..a105e67fa 100644 --- a/src/io.h +++ b/src/io.h @@ -1,10 +1,10 @@ #ifndef FISH_IO_H #define FISH_IO_H -#include +#include #include #include -#include +#include // Note that we have to include something to get any _LIBCPP_VERSION defined so we can detect libc++ // So it's key that vector go above. If we didn't need vector for other reasons, we might include // ciso646, which does nothing @@ -21,180 +21,124 @@ using std::tr1::shared_ptr; #include "common.h" -/** - Describes what type of IO operation an io_data_t represents -*/ -enum io_mode_t -{ - IO_FILE, IO_PIPE, IO_FD, IO_BUFFER, IO_CLOSE -}; +/// Describes what type of IO operation an io_data_t represents. +enum io_mode_t { IO_FILE, IO_PIPE, IO_FD, IO_BUFFER, IO_CLOSE }; -/** Represents an FD redirection */ -class io_data_t -{ -private: - /* No assignment or copying allowed */ +/// Represents an FD redirection. +class io_data_t { + private: + // No assignment or copying allowed. io_data_t(const io_data_t &rhs); void operator=(const io_data_t &rhs); -protected: - io_data_t(io_mode_t m, int f) : - io_mode(m), - fd(f) - { - } + protected: + io_data_t(io_mode_t m, int f) : io_mode(m), fd(f) {} -public: - /** Type of redirect */ + public: + /// Type of redirect. const io_mode_t io_mode; - /** FD to redirect */ + /// FD to redirect. const int fd; virtual void print() const = 0; virtual ~io_data_t() = 0; }; -class io_close_t : public io_data_t -{ -public: - explicit io_close_t(int f) : - io_data_t(IO_CLOSE, f) - { - } +class io_close_t : public io_data_t { + public: + explicit io_close_t(int f) : io_data_t(IO_CLOSE, f) {} virtual void print() const; }; -class io_fd_t : public io_data_t -{ -public: - /** fd to redirect specified fd to. For example, in 2>&1, old_fd is 1, and io_data_t::fd is 2 */ +class io_fd_t : public io_data_t { + public: + /// fd to redirect specified fd to. For example, in 2>&1, old_fd is 1, and io_data_t::fd is 2. const int old_fd; - - /** Whether this redirection was supplied by a script. For example, 'cmd <&3' would have user_supplied set to true. But a redirection that comes about through transmogrification would not. */ + + /// Whether this redirection was supplied by a script. For example, 'cmd <&3' would have + /// user_supplied set to true. But a redirection that comes about through transmogrification + /// would not. const bool user_supplied; - + virtual void print() const; - io_fd_t(int f, int old, bool us) : - io_data_t(IO_FD, f), - old_fd(old), - user_supplied(us) - { - } + io_fd_t(int f, int old, bool us) : io_data_t(IO_FD, f), old_fd(old), user_supplied(us) {} }; -class io_file_t : public io_data_t -{ -public: - /** Filename, malloc'd. This needs to be used after fork, so don't use wcstring here. */ - const char * const filename_cstr; - /** file creation flags to send to open */ +class io_file_t : public io_data_t { + public: + /// Filename, malloc'd. This needs to be used after fork, so don't use wcstring here. + const char *const filename_cstr; + /// file creation flags to send to open. const int flags; virtual void print() const; - io_file_t(int f, const wcstring &fname, int fl = 0) : - io_data_t(IO_FILE, f), - filename_cstr(wcs2str(fname)), - flags(fl) - { - } + io_file_t(int f, const wcstring &fname, int fl = 0) + : io_data_t(IO_FILE, f), filename_cstr(wcs2str(fname)), flags(fl) {} - virtual ~io_file_t() - { - free((void *)filename_cstr); - } + virtual ~io_file_t() { free((void *)filename_cstr); } }; -class io_pipe_t : public io_data_t -{ -protected: - io_pipe_t(io_mode_t m, int f, bool i): - io_data_t(m, f), - is_input(i) - { +class io_pipe_t : public io_data_t { + protected: + io_pipe_t(io_mode_t m, int f, bool i) : io_data_t(m, f), is_input(i) { pipe_fd[0] = pipe_fd[1] = -1; } -public: + public: int pipe_fd[2]; const bool is_input; virtual void print() const; - io_pipe_t(int f, bool i): - io_data_t(IO_PIPE, f), - is_input(i) - { - pipe_fd[0] = pipe_fd[1] = -1; - } + io_pipe_t(int f, bool i) : io_data_t(IO_PIPE, f), is_input(i) { pipe_fd[0] = pipe_fd[1] = -1; } }; class io_chain_t; -class io_buffer_t : public io_pipe_t -{ -private: - /** buffer to save output in */ +class io_buffer_t : public io_pipe_t { + private: + /// Buffer to save output in. std::vector out_buffer; - explicit io_buffer_t(int f): - io_pipe_t(IO_BUFFER, f, false /* not input */), - out_buffer() - { - } + explicit io_buffer_t(int f) : io_pipe_t(IO_BUFFER, f, false /* not input */), out_buffer() {} -public: + public: virtual void print() const; virtual ~io_buffer_t(); - /** Function to append to the buffer */ - void out_buffer_append(const char *ptr, size_t count) - { + /// Function to append to the buffer. + void out_buffer_append(const char *ptr, size_t count) { out_buffer.insert(out_buffer.end(), ptr, ptr + count); } - /** Function to get a pointer to the buffer */ - char *out_buffer_ptr(void) - { - return out_buffer.empty() ? NULL : &out_buffer.at(0); - } + /// Function to get a pointer to the buffer. + char *out_buffer_ptr(void) { return out_buffer.empty() ? NULL : &out_buffer.at(0); } - const char *out_buffer_ptr(void) const - { - return out_buffer.empty() ? NULL : &out_buffer.at(0); - } + const char *out_buffer_ptr(void) const { return out_buffer.empty() ? NULL : &out_buffer.at(0); } - /** Function to get the size of the buffer */ - size_t out_buffer_size(void) const - { - return out_buffer.size(); - } - - /* Ensures that the pipes do not conflict with any fd redirections in the chain */ + /// Function to get the size of the buffer. + size_t out_buffer_size(void) const { return out_buffer.size(); } + + /// Ensures that the pipes do not conflict with any fd redirections in the chain. bool avoid_conflicts_with_io_chain(const io_chain_t &ios); - /** - Close output pipe, and read from input pipe until eof. - */ + /// Close output pipe, and read from input pipe until eof. void read(); - /** - Create a IO_BUFFER type io redirection, complete with a pipe and a - vector for output. The default file descriptor used is STDOUT_FILENO - for buffering. - - \param fd the fd that will be mapped in the child process, typically STDOUT_FILENO - \param conflicts A set of IO redirections. The function ensures that any pipe it makes - does not conflict with an fd redirection in this list. - */ + /// Create a IO_BUFFER type io redirection, complete with a pipe and a vector for output. + /// The default file descriptor used is STDOUT_FILENO for buffering. + /// + /// \param fd the fd that will be mapped in the child process, typically STDOUT_FILENO + /// \param conflicts A set of IO redirections. The function ensures that any pipe it makes does + /// not conflict with an fd redirection in this list. static io_buffer_t *create(int fd, const io_chain_t &conflicts); }; -class io_chain_t : public std::vector > -{ -public: +class io_chain_t : public std::vector > { + public: io_chain_t(); explicit io_chain_t(const shared_ptr &); @@ -207,102 +151,79 @@ class io_chain_t : public std::vector > shared_ptr get_io_for_fd(int fd); }; - -/** - Return the last io redirection in the chain for the specified file descriptor. -*/ +/// Return the last io redirection in the chain for the specified file descriptor. shared_ptr io_chain_get(const io_chain_t &src, int fd); shared_ptr io_chain_get(io_chain_t &src, int fd); -/* Given a pair of fds, if an fd is used by the given io chain, duplicate that fd repeatedly until we find one that does not conflict, or we run out of fds. Returns the new fds by reference, closing the old ones. If we get an error, returns false (in which case both fds are closed and set to -1). */ +/// Given a pair of fds, if an fd is used by the given io chain, duplicate that fd repeatedly until +/// we find one that does not conflict, or we run out of fds. Returns the new fds by reference, +/// closing the old ones. If we get an error, returns false (in which case both fds are closed and +/// set to -1). bool pipe_avoid_conflicts_with_io_chain(int fds[2], const io_chain_t &ios); -/** Class representing the output that a builtin can generate */ -class output_stream_t -{ -private: - // no copying +/// Class representing the output that a builtin can generate. +class output_stream_t { + private: + // No copying. output_stream_t(const output_stream_t &s); void operator=(const output_stream_t &s); - + wcstring buffer_; - -public: - output_stream_t() - { - } - - void append(const wcstring &s) - { - this->buffer_.append(s); - } - - void append(const wchar_t *s) - { - this->buffer_.append(s); - } - - void append(wchar_t s) - { - this->buffer_.push_back(s); - } - - void append(const wchar_t *s, size_t amt) - { - this->buffer_.append(s, amt); - } - - void push_back(wchar_t c) - { - this->buffer_.push_back(c); - } - - void append_format(const wchar_t *format, ...) - { + + public: + output_stream_t() {} + + void append(const wcstring &s) { this->buffer_.append(s); } + + void append(const wchar_t *s) { this->buffer_.append(s); } + + void append(wchar_t s) { this->buffer_.push_back(s); } + + void append(const wchar_t *s, size_t amt) { this->buffer_.append(s, amt); } + + void push_back(wchar_t c) { this->buffer_.push_back(c); } + + void append_format(const wchar_t *format, ...) { va_list va; va_start(va, format); ::append_formatv(this->buffer_, format, va); va_end(va); } - - void append_formatv(const wchar_t *format, va_list va_orig) - { + + void append_formatv(const wchar_t *format, va_list va_orig) { ::append_formatv(this->buffer_, format, va_orig); } - - const wcstring &buffer() const - { - return this->buffer_; - } - - bool empty() const - { - return buffer_.empty(); - } + + const wcstring &buffer() const { return this->buffer_; } + + bool empty() const { return buffer_.empty(); } }; -struct io_streams_t -{ +struct io_streams_t { output_stream_t out; output_stream_t err; - + // fd representing stdin. This is not closed by the destructor. int stdin_fd; - - // Whether stdin is "directly redirected," meaning it is the recipient of a pipe (foo | cmd) or direct redirection (cmd < foo.txt) - // An "indirect redirection" would be e.g. begin ; cmd ; end < foo.txt + + // Whether stdin is "directly redirected," meaning it is the recipient of a pipe (foo | cmd) or + // direct redirection (cmd < foo.txt). An "indirect redirection" would be e.g. begin ; cmd ; end + // < foo.txt bool stdin_is_directly_redirected; - - // Indicates whether stdout and stderr are redirected (e.g. to a file or piped) + + // Indicates whether stdout and stderr are redirected (e.g. to a file or piped). bool out_is_redirected; bool err_is_redirected; - + // Actual IO redirections. This is only used by the source builtin. Unowned. const io_chain_t *io_chain; - - io_streams_t() : stdin_fd(-1), stdin_is_directly_redirected(false), out_is_redirected(false), err_is_redirected(false), io_chain(NULL) - { - } + + io_streams_t() + : stdin_fd(-1), + stdin_is_directly_redirected(false), + out_is_redirected(false), + err_is_redirected(false), + io_chain(NULL) {} }; #if 0 From 483b7988632d864c65e4b68fc4a92133b172bbfe Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sun, 1 May 2016 21:01:00 -0700 Subject: [PATCH 193/363] restyle iothread module to match project style Reduces lint errors from 41 to 26 (-37%). Line count from 444 to 423 (-5%). Another step in resolving issue #2902. --- src/iothread.cpp | 284 +++++++++++++++++++++++------------------------ src/iothread.h | 64 +++++------ 2 files changed, 166 insertions(+), 182 deletions(-) diff --git a/src/iothread.cpp b/src/iothread.cpp index 3623850e3..96a926a25 100644 --- a/src/iothread.cpp +++ b/src/iothread.cpp @@ -1,17 +1,17 @@ -#include #include -#include +#include #include +#include +#include +#include +#include #include #include #include -#include -#include #include -#include -#include "iothread.h" #include "common.h" +#include "iothread.h" #ifdef _POSIX_THREAD_THREADS_MAX #if _POSIX_THREAD_THREADS_MAX < 64 @@ -23,7 +23,7 @@ #define IO_MAX_THREADS 64 #endif -/* Values for the wakeup bytes sent to the ioport */ +// Values for the wakeup bytes sent to the ioport. #define IO_SERVICE_MAIN_THREAD_REQUEST_QUEUE 99 #define IO_SERVICE_RESULT_QUEUE 100 @@ -32,23 +32,22 @@ static void iothread_service_main_thread_requests(void); static void iothread_service_result_queue(); -struct SpawnRequest_t -{ +struct SpawnRequest_t { int (*handler)(void *); void (*completionCallback)(void *, int); void *context; int handlerResult; }; -struct MainThreadRequest_t -{ +struct MainThreadRequest_t { int (*handler)(void *); void *context; volatile int handlerResult; volatile bool done; }; -/* Spawn support. Requests are allocated and come in on request_queue. They go out on result_queue, at which point they can be deallocated. s_active_thread_count is also protected by the lock. */ +// Spawn support. Requests are allocated and come in on request_queue. They go out on result_queue, +// at which point they can be deallocated. s_active_thread_count is also protected by the lock. static pthread_mutex_t s_spawn_queue_lock; static std::queue s_request_queue; static int s_active_thread_count; @@ -56,140 +55,134 @@ static int s_active_thread_count; static pthread_mutex_t s_result_queue_lock; static std::queue s_result_queue; -/* "Do on main thread" support */ -static pthread_mutex_t s_main_thread_performer_lock; // protects the main thread requests -static pthread_cond_t s_main_thread_performer_condition; //protects the main thread requests -static pthread_mutex_t s_main_thread_request_queue_lock; // protects the queue +// "Do on main thread" support. +static pthread_mutex_t s_main_thread_performer_lock; // protects the main thread requests +static pthread_cond_t s_main_thread_performer_condition; // protects the main thread requests +static pthread_mutex_t s_main_thread_request_queue_lock; // protects the queue static std::queue s_main_thread_request_queue; -/* Notifying pipes */ +// Notifying pipes. static int s_read_pipe, s_write_pipe; -static void iothread_init(void) -{ +static void iothread_init(void) { static bool inited = false; - if (! inited) - { + if (!inited) { inited = true; - /* Initialize some locks */ + // Initialize some locks. VOMIT_ON_FAILURE(pthread_mutex_init(&s_spawn_queue_lock, NULL)); VOMIT_ON_FAILURE(pthread_mutex_init(&s_result_queue_lock, NULL)); VOMIT_ON_FAILURE(pthread_mutex_init(&s_main_thread_request_queue_lock, NULL)); VOMIT_ON_FAILURE(pthread_mutex_init(&s_main_thread_performer_lock, NULL)); VOMIT_ON_FAILURE(pthread_cond_init(&s_main_thread_performer_condition, NULL)); - /* Initialize the completion pipes */ + // 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. + // 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)); } } -static void add_to_queue(struct SpawnRequest_t *req) -{ +static void add_to_queue(struct SpawnRequest_t *req) { ASSERT_IS_LOCKED(s_spawn_queue_lock); s_request_queue.push(req); } -static SpawnRequest_t *dequeue_spawn_request(void) -{ +static SpawnRequest_t *dequeue_spawn_request(void) { ASSERT_IS_LOCKED(s_spawn_queue_lock); SpawnRequest_t *result = NULL; - if (! s_request_queue.empty()) - { + if (!s_request_queue.empty()) { result = s_request_queue.front(); s_request_queue.pop(); } return result; } -static void enqueue_thread_result(SpawnRequest_t *req) -{ +static void enqueue_thread_result(SpawnRequest_t *req) { scoped_lock lock(s_result_queue_lock); s_result_queue.push(req); } -static void *this_thread() -{ - return (void *)(intptr_t)pthread_self(); -} +static void *this_thread() { return (void *)(intptr_t)pthread_self(); } -/* The function that does thread work. */ -static void *iothread_worker(void *unused) -{ +/// The function that does thread work. +static void *iothread_worker(void *unused) { scoped_lock locker(s_spawn_queue_lock); struct SpawnRequest_t *req; - while ((req = dequeue_spawn_request()) != NULL) - { + while ((req = dequeue_spawn_request()) != NULL) { IOTHREAD_LOG fprintf(stderr, "pthread %p dequeued %p\n", this_thread(), req); - /* Unlock the queue while we execute the request */ + // Unlock the queue while we execute the request. locker.unlock(); - - /* Perfor the work */ + + // Perform the work. req->handlerResult = req->handler(req->context); - - /* If there's a completion handler, we have to enqueue it on the result queue. Otherwise, we can just delete the request! */ - if (req->completionCallback == NULL) - { + + // If there's a completion handler, we have to enqueue it on the result queue. Otherwise, we + // can just delete the request! + if (req->completionCallback == NULL) { delete req; - } - else - { - /* Enqueue the result, and tell the main thread about it */ + } else { + // Enqueue the result, and tell the main thread about it. enqueue_thread_result(req); const char wakeup_byte = IO_SERVICE_RESULT_QUEUE; - VOMIT_ON_FAILURE(! write_loop(s_write_pipe, &wakeup_byte, sizeof wakeup_byte)); + VOMIT_ON_FAILURE(!write_loop(s_write_pipe, &wakeup_byte, sizeof wakeup_byte)); } - - /* Lock us up again */ + + // Lock us up again. locker.lock(); } - - /* We believe we have exhausted the thread request queue. We want to decrement s_active_thread_count and exit. But it's possible that a request just came in. Furthermore, it's possible that the main thread saw that s_active_thread_count is full, and decided to not spawn a new thread, trusting in one of the existing threads to handle it. But we've already committed to not handling anything else. Therefore, we have to decrement s_active_thread_count under the lock, which we still hold. Likewise, the main thread must check the value under the lock. */ + + // We believe we have exhausted the thread request queue. We want to decrement + // s_active_thread_count and exit. But it's possible that a request just came in. Furthermore, + // it's possible that the main thread saw that s_active_thread_count is full, and decided to not + // spawn a new thread, trusting in one of the existing threads to handle it. But we've already + // committed to not handling anything else. Therefore, we have to decrement + // s_active_thread_count under the lock, which we still hold. Likewise, the main thread must + // check the value under the lock. ASSERT_IS_LOCKED(s_spawn_queue_lock); assert(s_active_thread_count > 0); s_active_thread_count -= 1; - - IOTHREAD_LOG fprintf(stderr, "pthread %p exiting\n", this_thread()); - /* We're done */ + IOTHREAD_LOG fprintf(stderr, "pthread %p exiting\n", this_thread()); + // We're done. return NULL; } -/* Spawn another thread. No lock is held when this is called. */ -static void iothread_spawn() -{ - /* 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. */ +/// Spawn another thread. No lock is held when this is called. +static void iothread_spawn() { + // 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 new_set, saved_set; sigfillset(&new_set); VOMIT_ON_FAILURE(pthread_sigmask(SIG_BLOCK, &new_set, &saved_set)); - /* Spawn a thread. If this fails, it means there's already a bunch of threads; it is very unlikely that they are all on the verge of exiting, so one is likely to be ready to handle extant requests. So we can ignore failure with some confidence. */ + // Spawn a thread. If this fails, it means there's already a bunch of threads; it is very + // unlikely that they are all on the verge of exiting, so one is likely to be ready to handle + // extant requests. So we can ignore failure with some confidence. pthread_t thread = 0; pthread_create(&thread, NULL, iothread_worker, NULL); - - /* We will never join this thread */ + + // We will never join this thread. VOMIT_ON_FAILURE(pthread_detach(thread)); - IOTHREAD_LOG fprintf(stderr, "pthread %p spawned\n", (void *)(intptr_t)thread); - - /* Restore our sigmask */ + // Restore our sigmask. VOMIT_ON_FAILURE(pthread_sigmask(SIG_SETMASK, &saved_set, 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(); - /* Create and initialize a request. */ + // Create and initialize a request. struct SpawnRequest_t *req = new SpawnRequest_t(); req->handler = handler; req->completionCallback = completionCallback; @@ -198,59 +191,56 @@ int iothread_perform_base(int (*handler)(void *), void (*completionCallback)(voi int local_thread_count = -1; bool spawn_new_thread = false; { - /* Lock around a local region. Note that we can only access s_active_thread_count under the lock. */ + // Lock around a local region. Note that we can only access s_active_thread_count under the + // lock. scoped_lock lock(s_spawn_queue_lock); add_to_queue(req); - if (s_active_thread_count < IO_MAX_THREADS) - { + if (s_active_thread_count < IO_MAX_THREADS) { s_active_thread_count++; spawn_new_thread = true; } local_thread_count = s_active_thread_count; } - - /* Kick off the thread if we decided to do so */ - if (spawn_new_thread) - { + + // Kick off the thread if we decided to do so. + if (spawn_new_thread) { iothread_spawn(); } - - /* We return the active thread count for informational purposes only */ + + // We return the active thread count for informational purposes only. return local_thread_count; } -int iothread_port(void) -{ +int iothread_port(void) { iothread_init(); return s_read_pipe; } -void iothread_service_completion(void) -{ +void iothread_service_completion(void) { ASSERT_IS_MAIN_THREAD(); char wakeup_byte = 0; VOMIT_ON_FAILURE(1 != read_loop(iothread_port(), &wakeup_byte, sizeof wakeup_byte)); - switch (wakeup_byte) - { - case IO_SERVICE_MAIN_THREAD_REQUEST_QUEUE: + switch (wakeup_byte) { + case IO_SERVICE_MAIN_THREAD_REQUEST_QUEUE: { iothread_service_main_thread_requests(); break; - case IO_SERVICE_RESULT_QUEUE: + } + case IO_SERVICE_RESULT_QUEUE: { iothread_service_result_queue(); break; - default: + } + default: { fprintf(stderr, "Unknown wakeup byte %02x in %s\n", wakeup_byte, __FUNCTION__); break; + } } } -static bool iothread_wait_for_pending_completions(long timeout_usec) -{ +static bool iothread_wait_for_pending_completions(long timeout_usec) { const long usec_per_sec = 1000000; struct timeval tv; tv.tv_sec = timeout_usec / usec_per_sec; tv.tv_usec = timeout_usec % usec_per_sec; - const int fd = iothread_port(); fd_set fds; FD_ZERO(&fds); @@ -259,29 +249,31 @@ static bool iothread_wait_for_pending_completions(long timeout_usec) return ret > 0; } -/* Note that this function is quite sketchy. In particular, it drains threads, not requests, meaning that it may leave requests on the queue. This is the desired behavior (it may be called before fork, and we don't want to bother servicing requests before we fork), but in the test suite we depend on it draining all requests. In practice, this works, because a thread in practice won't exit while there is outstanding requests. - - At the moment, this function is only used in the test suite and in a drain-all-threads-before-fork compatibility mode that no architecture requires, so it's OK that it's terrible. -*/ -void iothread_drain_all(void) -{ +/// Note that this function is quite sketchy. In particular, it drains threads, not requests, +/// meaning that it may leave requests on the queue. This is the desired behavior (it may be called +/// before fork, and we don't want to bother servicing requests before we fork), but in the test +/// suite we depend on it draining all requests. In practice, this works, because a thread in +/// practice won't exit while there is outstanding requests. +/// +/// At the moment, this function is only used in the test suite and in a +/// drain-all-threads-before-fork compatibility mode that no architecture requires, so it's OK that +/// it's terrible. +void iothread_drain_all(void) { ASSERT_IS_MAIN_THREAD(); ASSERT_IS_NOT_FORKED_CHILD(); - + scoped_lock locker(s_spawn_queue_lock); - + #define TIME_DRAIN 0 #if TIME_DRAIN int thread_count = s_active_thread_count; double now = timef(); #endif - - /* Nasty polling via select(). */ - while (s_active_thread_count > 0) - { + + // Nasty polling via select(). + while (s_active_thread_count > 0) { locker.unlock(); - if (iothread_wait_for_pending_completions(1000)) - { + if (iothread_wait_for_pending_completions(1000)) { iothread_service_completion(); } locker.lock(); @@ -292,70 +284,64 @@ void iothread_drain_all(void) #endif } -/* "Do on main thread" support */ -static void iothread_service_main_thread_requests(void) -{ +/// "Do on main thread" support. +static void iothread_service_main_thread_requests(void) { ASSERT_IS_MAIN_THREAD(); - // Move the queue to a local variable + // Move the queue to a local variable. std::queue request_queue; { scoped_lock queue_lock(s_main_thread_request_queue_lock); std::swap(request_queue, s_main_thread_request_queue); } - if (! request_queue.empty()) - { - // Perform each of the functions - // Note we are NOT responsible for deleting these. They are stack allocated in their respective threads! - while (! request_queue.empty()) - { + if (!request_queue.empty()) { + // Perform each of the functions. Note we are NOT responsible for deleting these. They are + // stack allocated in their respective threads! + while (!request_queue.empty()) { MainThreadRequest_t *req = request_queue.front(); request_queue.pop(); req->handlerResult = req->handler(req->context); req->done = true; } - /* Ok, we've handled everybody. Announce the good news, and allow ourselves to be unlocked. Note we must do this while holding the lock. Otherwise we race with the waiting threads: - 1. waiting thread checks for done, sees false - 2. main thread performs request, sets done to true, posts to condition - 3. waiting thread unlocks lock, waits on condition (forever) - Because the waiting thread performs step 1 under the lock, if we take the lock, we avoid posting before the waiting thread is waiting. - */ + // Ok, we've handled everybody. Announce the good news, and allow ourselves to be unlocked. + // Note we must do this while holding the lock. Otherwise we race with the waiting threads: + // + // 1. waiting thread checks for done, sees false + // 2. main thread performs request, sets done to true, posts to condition + // 3. waiting thread unlocks lock, waits on condition (forever) + // + // Because the waiting thread performs step 1 under the lock, if we take the lock, we avoid + // posting before the waiting thread is waiting. scoped_lock broadcast_lock(s_main_thread_performer_lock); VOMIT_ON_FAILURE(pthread_cond_broadcast(&s_main_thread_performer_condition)); } } /* Service the queue of results */ -static void iothread_service_result_queue() -{ - // Move the queue to a local variable +static void iothread_service_result_queue() { + // Move the queue to a local variable. std::queue result_queue; { scoped_lock queue_lock(s_result_queue_lock); std::swap(result_queue, s_result_queue); } - - // Perform each completion in order - // We are responsibile for cleaning them up - while (! result_queue.empty()) - { + + // Perform each completion in order. We are responsibile for cleaning them up. + while (!result_queue.empty()) { SpawnRequest_t *req = result_queue.front(); result_queue.pop(); - if (req->completionCallback) - { + if (req->completionCallback) { req->completionCallback(req->context, req->handlerResult); } delete req; } } -int iothread_perform_on_main_base(int (*handler)(void *), void *context) -{ - // If this is the main thread, just do it - if (is_main_thread()) - { +int iothread_perform_on_main_base(int (*handler)(void *), void *context) { + // If this is the main thread, just do it. + if (is_main_thread()) { return handler(context); } @@ -366,25 +352,27 @@ int iothread_perform_on_main_base(int (*handler)(void *), void *context) req.handlerResult = 0; req.done = false; - // Append it + // Append it. Do not delete the nested scope as it is crucial to the proper functioning of this + // code by virtue of the lock management. { scoped_lock queue_lock(s_main_thread_request_queue_lock); s_main_thread_request_queue.push(&req); } - // Tell the pipe + // Tell the pipe. const char wakeup_byte = IO_SERVICE_MAIN_THREAD_REQUEST_QUEUE; - VOMIT_ON_FAILURE(! write_loop(s_write_pipe, &wakeup_byte, sizeof wakeup_byte)); + VOMIT_ON_FAILURE(!write_loop(s_write_pipe, &wakeup_byte, sizeof wakeup_byte)); - // Wait on the condition, until we're done + // Wait on the condition, until we're done. scoped_lock perform_lock(s_main_thread_performer_lock); - while (! req.done) - { - // It would be nice to support checking for cancellation here, but the clients need a deterministic way to clean up to avoid leaks - VOMIT_ON_FAILURE(pthread_cond_wait(&s_main_thread_performer_condition, &s_main_thread_performer_lock)); + while (!req.done) { + // It would be nice to support checking for cancellation here, but the clients need a + // deterministic way to clean up to avoid leaks + VOMIT_ON_FAILURE( + pthread_cond_wait(&s_main_thread_performer_condition, &s_main_thread_performer_lock)); } - // Ok, the request must now be done + // Ok, the request must now be done. assert(req.done); return req.handlerResult; } diff --git a/src/iothread.h b/src/iothread.h index 26b775505..ec57be683 100644 --- a/src/iothread.h +++ b/src/iothread.h @@ -1,54 +1,50 @@ -/** \file iothread.h - Handles IO that may hang. -*/ +// Handles IO that may hang. #ifndef FISH_IOTHREAD_H #define FISH_IOTHREAD_H -/** - Runs a command on a thread. +/// Runs a command on a thread. +/// +/// \param handler The function to execute on a background thread. Accepts an arbitrary context +/// pointer, and returns an int, which is passed to the completionCallback. +/// \param completionCallback The function to execute on the main thread once the background thread +/// is complete. Accepts an int (the return value of handler) and the context. +/// \param context A arbitary context pointer to pass to the handler and completion callback. +/// \return A sequence number, currently not very useful. +int iothread_perform_base(int (*handler)(void *), void (*completionCallback)(void *, int), + void *context); - \param handler The function to execute on a background thread. Accepts an arbitrary context pointer, and returns an int, which is passed to the completionCallback. - \param completionCallback The function to execute on the main thread once the background thread is complete. Accepts an int (the return value of handler) and the context. - \param context A arbitary context pointer to pass to the handler and completion callback. - \return A sequence number, currently not very useful. -*/ -int iothread_perform_base(int (*handler)(void *), void (*completionCallback)(void *, int), void *context); - -/** - Gets the fd on which to listen for completion callbacks. - - \return A file descriptor on which to listen for completion callbacks. -*/ +/// Gets the fd on which to listen for completion callbacks. +/// +/// \return A file descriptor on which to listen for completion callbacks. int iothread_port(void); -/** Services one iothread competion callback. */ +/// Services one iothread competion callback. void iothread_service_completion(void); -/** Waits for all iothreads to terminate. */ +/// Waits for all iothreads to terminate. void iothread_drain_all(void); -/** Performs a function on the main thread, blocking until it completes */ +/// Performs a function on the main thread, blocking until it completes. int iothread_perform_on_main_base(int (*handler)(void *), void *context); -/** Helper templates */ -template -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)); +/// Helper templates. +template +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)); } -/* Variant that takes no completion callback */ -template -int iothread_perform(int (*handler)(T *), T *context) -{ - return iothread_perform_base((int (*)(void *))handler, (void (*)(void *, int))0, static_cast(context)); +// Variant that takes no completion callback. +template +int iothread_perform(int (*handler)(T *), T *context) { + return iothread_perform_base((int (*)(void *))handler, (void (*)(void *, int))0, + static_cast(context)); } -template -int iothread_perform_on_main(int (*handler)(T *), T *context) -{ +template +int iothread_perform_on_main(int (*handler)(T *), T *context) { return iothread_perform_on_main_base((int (*)(void *))handler, (void *)(context)); } - #endif From 4f619c966bdfc8ca27a6ccc5a4020f5aa5c81c30 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sun, 1 May 2016 22:16:00 -0700 Subject: [PATCH 194/363] restyle kill module to match project style Reduces lint errors from 10 to 9 (-10%). Line count from 242 to 175 (-28%). Another step in resolving issue #2902. --- key_reader | Bin 0 -> 1470480 bytes src/kill.cpp | 173 ++++++++++++++++----------------------------------- src/kill.h | 26 ++++---- 3 files changed, 66 insertions(+), 133 deletions(-) create mode 100755 key_reader diff --git a/key_reader b/key_reader new file mode 100755 index 0000000000000000000000000000000000000000..96f519eae333c4f02ca3d3032d46ba9ee37151d4 GIT binary patch literal 1470480 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%0AV@j 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
    #3hi!p#}C-n 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 z%0A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 ) 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

    Uby3A+en8Fj-v=;lZWn2U3=kYq663&5 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

    mYy0CNC{VN9yoh4$PVp&WS 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 literal 0 HcmV?d00001 diff --git a/src/kill.cpp b/src/kill.cpp index fb83c8aad..c846f5f55 100644 --- a/src/kill.cpp +++ b/src/kill.cpp @@ -1,85 +1,65 @@ -/** \file kill.c - The killring. - - Works like the killring in emacs and readline. The killring is cut - and paste with a memory of previous cuts. It supports integration - with the X clipboard. -*/ +// The killring. +// +// Works like the killring in emacs and readline. The killring is cut and paste with a memory of +// previous cuts. It supports integration with the X clipboard. +#include #include #include #include -#include #include -#include +#include -#include "fallback.h" // IWYU pragma: keep -#include "kill.h" #include "common.h" #include "env.h" #include "exec.h" +#include "fallback.h" // IWYU pragma: keep +#include "kill.h" #include "path.h" /** Kill ring */ typedef std::list kill_list_t; static kill_list_t kill_list; -/** - Contents of the X clipboard, at last time we checked it -*/ +/// Contents of the X clipboard, at last time we checked it. static wcstring cut_buffer; -/** - Test if the xsel command is installed. Since this is called often, - cache the result. -*/ -static int has_xsel() -{ - static signed char res=-1; - if (res < 0) - { - res = !! path_get_path(L"xsel", NULL); +/// Test if the xsel command is installed. Since this is called often, cache the result. +static int has_xsel() { + static signed char res = -1; + if (res < 0) { + res = !!path_get_path(L"xsel", NULL); } return res; } -void kill_add(const wcstring &str) -{ +void kill_add(const wcstring &str) { ASSERT_IS_MAIN_THREAD(); - if (str.empty()) - return; + if (str.empty()) return; wcstring cmd; wcstring escaped_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. - - 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. - */ - + // 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. const env_var_t clipboard_wstr = env_get_string(L"FISH_CLIPBOARD_CMD"); - if (!clipboard_wstr.missing()) - { + if (!clipboard_wstr.missing()) { escaped_str = escape(str.c_str(), ESCAPE_ALL); 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()) - { + } 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()) - { + if (!disp_wstr.missing()) { escaped_str = escape(str.c_str(), ESCAPE_ALL); cmd.assign(L"echo -n "); cmd.append(escaped_str); @@ -87,89 +67,59 @@ void kill_add(const wcstring &str) } } - if (! cmd.empty()) - { - if (exec_subshell(cmd, false /* do not apply exit status */) == -1) - { - /* - Do nothing on failiure - */ + if (!cmd.empty()) { + if (exec_subshell(cmd, false /* do not apply exit status */) == -1) { + // Do nothing on failure. } - cut_buffer = escaped_str; } } -/** - Remove first match for specified string from circular list -*/ -static void kill_remove(const wcstring &s) -{ +/// Remove first match for specified string from circular list. +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); - if (iter != kill_list.end()) - kill_list.erase(iter); + if (iter != kill_list.end()) kill_list.erase(iter); } - - -void kill_replace(const wcstring &old, const wcstring &newv) -{ +void kill_replace(const wcstring &old, const wcstring &newv) { kill_remove(old); kill_add(newv); } -const wchar_t *kill_yank_rotate() -{ +const wchar_t *kill_yank_rotate() { ASSERT_IS_MAIN_THREAD(); - // Move the first element to the end - if (kill_list.empty()) - { + // Move the first element to the end. + 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(); } } -/** - Check the X clipboard. If it has been changed, add the new - clipboard contents to the fish killring. -*/ -static void kill_check_x_buffer() -{ - if (!has_xsel()) - return; +/// Check the X clipboard. If it has been changed, add the new clipboard contents to the fish +/// killring. +static void kill_check_x_buffer() { + if (!has_xsel()) return; const env_var_t disp = env_get_string(L"DISPLAY"); - if (! disp.missing()) - { + if (!disp.missing()) { size_t i; wcstring cmd = L"xsel -t 500 -b"; - wcstring new_cut_buffer=L""; + wcstring new_cut_buffer = L""; wcstring_list_t list; - if (exec_subshell(cmd, list, false /* do not apply exit status */) != -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 (cut_buffer != new_cut_buffer) - { + 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 != new_cut_buffer) { cut_buffer = new_cut_buffer; kill_list.push_front(new_cut_buffer); } @@ -178,30 +128,17 @@ static void kill_check_x_buffer() } } - -const wchar_t *kill_yank() -{ +const wchar_t *kill_yank() { kill_check_x_buffer(); - if (kill_list.empty()) - { + if (kill_list.empty()) { return L""; - } - else - { + } else { return kill_list.front().c_str(); } } -void kill_sanity_check() -{ -} +void kill_sanity_check() {} -void kill_init() -{ -} - -void kill_destroy() -{ - cut_buffer.clear(); -} +void kill_init() {} +void kill_destroy() { cut_buffer.clear(); } diff --git a/src/kill.h b/src/kill.h index cdcdf6915..ed0fc112c 100644 --- a/src/kill.h +++ b/src/kill.h @@ -1,35 +1,31 @@ -/** \file kill.h - Prototypes for the killring. - - Works like the killring in emacs and readline. The killring is cut and paste whith a memory of previous cuts. -*/ +// Prototypes for the killring. +// +// Works like the killring in emacs and readline. The killring is cut and paste whith a memory of +// previous cuts. #ifndef FISH_KILL_H #define FISH_KILL_H #include "common.h" -/** - Replace the specified string in the killring -*/ +/// Replace the specified string in the killring. void kill_replace(const wcstring &old, const wcstring &newv); - -/** Add a string to the top of the killring */ +/// Add a string to the top of the killring. void kill_add(const wcstring &str); -/** Rotate the killring */ +/// Rotate the killring. const wchar_t *kill_yank_rotate(); -/** Paste from the killring */ +/// Paste from the killring. const wchar_t *kill_yank(); -/** Sanity check */ +/// Sanity check. void kill_sanity_check(); -/** Initialize the killring */ +/// Initialize the killring. void kill_init(); -/** Destroy the killring */ +/// Destroy the killring. void kill_destroy(); #endif From ed8d1040bafde3d43439ac1d24631a35d1f96810 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sun, 1 May 2016 22:29:21 -0700 Subject: [PATCH 195/363] restyle output module to match project style Reduces lint errors from 34 to 31 (-9%). Line count from 712 to 535 (-25%). Another step in resolving issue #2902. --- src/output.cpp | 440 +++++++++++++++++-------------------------------- src/output.h | 131 ++++++--------- 2 files changed, 197 insertions(+), 374 deletions(-) diff --git a/src/output.cpp b/src/output.cpp index 3fda2e67e..935efe96c 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -1,10 +1,8 @@ -/** \file output.c - Generic output functions - */ +// Generic output functions. #include "config.h" -#include #include +#include #include #if HAVE_NCURSES_H #include @@ -18,87 +16,61 @@ #elif HAVE_NCURSES_TERM_H #include #endif -#include #include +#include +#include #include #include -#include -#include "fallback.h" // IWYU pragma: keep -#include "wutil.h" // IWYU pragma: keep -#include "common.h" -#include "output.h" #include "color.h" +#include "common.h" +#include "fallback.h" // IWYU pragma: keep +#include "output.h" +#include "wutil.h" // IWYU pragma: keep static int writeb_internal(char c); -/** - The function used for output - */ - +/// The function used for output. static int (*out)(char c) = &writeb_internal; -/** - Name of terminal - */ +/// Name of terminal. static wcstring current_term; -/* Whether term256 and term24bit are supported */ +/// Whether term256 and term24bit are supported. static color_support_t color_support = 0; - -void output_set_writer(int (*writer)(char)) -{ - CHECK(writer,); +void output_set_writer(int (*writer)(char)) { + CHECK(writer, ); out = writer; } -int (*output_get_writer())(char) -{ - return out; -} +int (*output_get_writer())(char) { return out; } -static bool term256_support_is_native(void) -{ - /* Return YES if we think the term256 support is "native" as opposed to forced. */ +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; } -color_support_t output_get_color_support(void) -{ - return color_support; -} +color_support_t output_get_color_support(void) { return color_support; } -void output_set_color_support(color_support_t val) -{ - color_support = val; -} +void output_set_color_support(color_support_t val) { color_support = val; } -unsigned char index_for_color(rgb_color_t c) -{ - if (c.is_named() || ! (output_get_color_support() & color_support_term256)) - { +unsigned char index_for_color(rgb_color_t c) { + if (c.is_named() || !(output_get_color_support() & color_support_term256)) { return c.to_name_index(); - } - else - { + } else { return c.to_term256_index(); } } - -static bool write_color_escape(char *todo, unsigned char idx, bool is_fg) -{ +static bool write_color_escape(char *todo, unsigned char idx, bool is_fg) { bool result = false; - if (idx < 16 || term256_support_is_native()) - { - /* Use tparm */ + if (idx < 16 || term256_support_is_native()) { + // Use tparm. writembs(tparm(todo, idx)); result = true; - } - else - { - /* We are attempting to bypass the term here. Generate the ANSI escape sequence ourself. */ + } else { + // We are attempting to bypass the term here. Generate the ANSI escape sequence ourself. char stridx[128]; format_long_safe(stridx, idx); char buff[128] = "\x1b["; @@ -107,10 +79,8 @@ static bool write_color_escape(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]); } } @@ -120,70 +90,50 @@ static bool write_color_escape(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_escape(set_a_foreground, idx, true); - } - else if (set_foreground && set_foreground[0]) - { + } else if (set_foreground && set_foreground[0]) { return write_color_escape(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_escape(set_a_background, idx, false); - } - else if (set_background && set_background[0]) - { + } else if (set_background && set_background[0]) { return write_color_escape(set_background, idx, false); - } - else - { + } else { return false; } } -void write_color(rgb_color_t color, bool is_fg) -{ - bool supports_term24bit = !! (output_get_color_support() & color_support_term24bit); - if (! supports_term24bit || ! color.is_rgb()) - { - /* Indexed or non-24 bit color */ +void write_color(rgb_color_t color, bool is_fg) { + bool supports_term24bit = !!(output_get_color_support() & color_support_term24bit); + if (!supports_term24bit || !color.is_rgb()) { + // Indexed or non-24 bit color. unsigned char idx = index_for_color(color); (is_fg ? write_foreground_color : write_background_color)(idx); - } - else - { - /* 24 bit! No tparm here, just ANSI escape sequences. - Foreground: ^[38;2;;;m - Background: ^[48;2;;;m - */ + } else { + // 24 bit! No tparm here, just ANSI escape sequences. + // Foreground: ^[38;2;;;m + // Background: ^[48;2;;;m color24_t rgb = color.to_color24(); char buff[128]; - snprintf(buff, sizeof buff, "\x1b[%u;2;%u;%u;%um", is_fg ? 38 : 48, rgb.rgb[0], rgb.rgb[1], rgb.rgb[2]); + snprintf(buff, sizeof buff, "\x1b[%u;2;%u;%u;%um", is_fg ? 38 : 48, rgb.rgb[0], rgb.rgb[1], + rgb.rgb[2]); 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]); } } } } -void set_color(rgb_color_t c, rgb_color_t c2) -{ - +void set_color(rgb_color_t c, rgb_color_t c2) { #if 0 wcstring tmp = c.description(); wcstring tmp2 = c2.description(); @@ -194,239 +144,180 @@ 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 int was_bold = 0; + static int was_underline = 0; + int bg_set = 0, last_bg_set = 0; int is_bold = 0; int is_underline = 0; - /* - Test if we have at least basic support for setting fonts, colors - and related bits - otherwise just give up... - */ - if (!exit_attribute_mode) - { + // Test if we have at least basic support for setting fonts, colors and related bits - otherwise + // just give up... + if (!exit_attribute_mode) { return; } - is_bold |= c.is_bold(); is_bold |= c2.is_bold(); is_underline |= c.is_underline(); is_underline |= c2.is_underline(); - if (c.is_reset() || c2.is_reset()) - { + 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... - */ + was_bold = 0; + was_underline = 0; + // If we exit attibute mode, we must first set a color, or previously coloured text might + // lose it's color. Terminals are weird... write_foreground_color(0); writembs(exit_attribute_mode); return; } - if (was_bold && !is_bold) - { - /* - Only way to exit bold mode is a reset of all attributes. - */ + 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; + was_bold = 0; + was_underline = 0; } - if (!last_color2.is_normal() && !last_color2.is_reset()) - { + if (!last_color2.is_normal() && !last_color2.is_reset()) { // Background was set. last_bg_set = 1; } - if (!c2.is_normal()) - { + if (!c2.is_normal()) { // Background is set. bg_set = 1; - if (c == c2) c = (c2==rgb_color_t::white())?rgb_color_t::black():rgb_color_t::white(); + 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. - */ + 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 - */ + 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(); + 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 (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()) - { + was_bold = 0; + was_underline = 0; + } else if (!c.is_special()) { write_color(c, true /* foreground */); } } 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()) - { + if (!last_color.is_normal()) { write_color(last_color, true /* foreground */); } - - was_bold=0; - was_underline=0; + was_bold = 0; + was_underline = 0; last_color2 = c2; - } - else if (! c2.is_special()) - { + } else if (!c2.is_special()) { write_color(c2, false /* not foreground */); last_color2 = c2; } } - /* - Lastly, we set bold mode and underline mode correctly - */ - if ((enter_bold_mode != 0) && (strlen(enter_bold_mode) > 0) && !bg_set) - { - if (is_bold && !was_bold) - { - if (enter_bold_mode) - { + // Lastly, we set bold mode and underline mode correctly. + if ((enter_bold_mode != 0) && (strlen(enter_bold_mode) > 0) && !bg_set) { + if (is_bold && !was_bold) { + if (enter_bold_mode) { writembs(tparm(enter_bold_mode)); } } was_bold = is_bold; } - if (was_underline && !is_underline) - { + if (was_underline && !is_underline) { writembs(exit_underline_mode); } - if (!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) -{ +/// Default output method, simply calls write() on stdout. +static int writeb_internal(char c) { write_loop(1, &c, 1); return 0; } -int writeb(tputs_arg_t b) -{ +int writeb(tputs_arg_t b) { out(b); return 0; } -int writech(wint_t ch) -{ - char buff[MB_LEN_MAX+1]; +int writech(wint_t ch) { + char buff[MB_LEN_MAX + 1]; size_t len; - if (ch >= ENCODE_DIRECT_BASE && ch < ENCODE_DIRECT_BASE + 256) - { + if (ch >= ENCODE_DIRECT_BASE && ch < ENCODE_DIRECT_BASE + 256) { buff[0] = ch - ENCODE_DIRECT_BASE; len = 1; - } - else if (MB_CUR_MAX == 1) // single-byte locale (C/POSIX/ISO-8859) + } else if (MB_CUR_MAX == 1) // single-byte locale (C/POSIX/ISO-8859) { // If `wc` contains a wide character we emit a question-mark. - if (ch & ~0xFF) - { + if (ch & ~0xFF) { ch = '?'; } buff[0] = ch; len = 1; - } - else - { + } else { mbstate_t state = {}; len = wcrtomb(buff, ch, &state); - if (len == (size_t)-1) - { + if (len == (size_t)-1) { return 1; } } - for (size_t i = 0; i < len; i++) - { + for (size_t i = 0; i < len; i++) { out(buff[i]); } return 0; } -void writestr(const wchar_t *str) -{ - CHECK(str,); +void writestr(const wchar_t *str) { + CHECK(str, ); - if (MB_CUR_MAX == 1) // single-byte locale (C/POSIX/ISO-8859) + if (MB_CUR_MAX == 1) // single-byte locale (C/POSIX/ISO-8859) { - while( *str ) - { - writech( *str++ ); + while (*str) { + writech(*str++); } return; } size_t len = wcstombs(0, str, 0); // figure amount of space needed - if (len == (size_t)-1) - { + if (len == (size_t)-1) { debug(1, L"Tried to print invalid wide character string"); return; } @@ -439,86 +330,65 @@ void writestr(const wchar_t *str) else buffer = new char[len]; - wcstombs(buffer, - str, - len); + wcstombs(buffer, str, len); - /* - Write - */ - for (char *pos = buffer; *pos; pos++) - { + // Write the string. + for (char *pos = buffer; *pos; pos++) { out(*pos); } - if (buffer != static_buffer) - delete[] buffer; + if (buffer != static_buffer) delete[] buffer; } -rgb_color_t best_color(const std::vector &candidates, color_support_t support) -{ - if (candidates.empty()) - { +rgb_color_t best_color(const std::vector &candidates, color_support_t support) { + if (candidates.empty()) { return rgb_color_t::none(); } - + 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 (first_rgb.is_none() && color.is_rgb()) - { + if (first_rgb.is_none() && color.is_rgb()) { first_rgb = color; } - if (first_named.is_none() && color.is_named()) - { + if (first_named.is_none() && color.is_named()) { first_named = color; } } - // If we have both RGB and named colors, then prefer rgb if term256 is supported + // If we have both RGB and named colors, then prefer rgb if term256 is supported. rgb_color_t result = rgb_color_t::none(); - bool has_term256 = !! (support & color_support_term256); - if ((!first_rgb.is_none() && has_term256) || first_named.is_none()) - { + bool has_term256 = !!(support & color_support_term256); + if ((!first_rgb.is_none() && has_term256) || first_named.is_none()) { result = first_rgb; - } - else - { + } else { result = first_named; } - if (result.is_none()) - { + if (result.is_none()) { result = candidates.at(0); } return result; } -/* This code should be refactored to enable sharing with builtin_set_color */ -rgb_color_t parse_color(const wcstring &val, bool is_background) -{ - int is_bold=0; - int is_underline=0; +// This code should be refactored to enable sharing with builtin_set_color. +rgb_color_t parse_color(const wcstring &val, bool is_background) { + int is_bold = 0; + int is_underline = 0; std::vector candidates; wcstring_list_t 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) - { - // look for something like "--background=red" + 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") @@ -527,19 +397,16 @@ 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); } } } rgb_color_t result = best_color(candidates, output_get_color_support()); - - if (result.is_none()) - result = rgb_color_t::normal(); + + if (result.is_none()) result = rgb_color_t::normal(); result.set_bold(is_bold); result.set_underline(is_underline); @@ -552,29 +419,18 @@ rgb_color_t parse_color(const wcstring &val, bool is_background) return result; } -void output_set_term(const wcstring &term) -{ - current_term.assign(term); -} +void output_set_term(const wcstring &term) { current_term.assign(term); } -const wchar_t *output_get_term() -{ +const wchar_t *output_get_term() { return current_term.empty() ? L"" : current_term.c_str(); } -void writembs_check(char *mbs, const char *mbs_name, const char *file, long line) -{ - if (mbs != NULL) - { +void writembs_check(char *mbs, const char *mbs_name, const char *file, long line) { + if (mbs != NULL) { tputs(mbs, 1, &writeb); - } - else - { - debug( 0, _(L"Tried to use terminfo string %s on line %ld of %s, which is undefined in terminal of type \"%ls\". Please report this error to %s"), - mbs_name, - line, - file, - output_get_term(), - PACKAGE_BUGREPORT); + } else { + debug(0, _(L"Tried to use terminfo string %s on line %ld of %s, which is undefined in " + L"terminal of type \"%ls\". Please report this error to %s"), + mbs_name, line, file, output_get_term(), PACKAGE_BUGREPORT); } } diff --git a/src/output.h b/src/output.h index 7f5b077d4..5882676df 100644 --- a/src/output.h +++ b/src/output.h @@ -1,25 +1,20 @@ -/** \file output.h - Generic output functions -*/ -/** - Constants for various character classifications. Each character of a command string can be classified as one of the following types. -*/ +// Generic output functions. +// +// Constants for various character classifications. Each character of a command string can be +// classified as one of the following types. #ifndef FISH_OUTPUT_H #define FISH_OUTPUT_H -#include -#include #include +#include +#include +#include "color.h" #include "common.h" #include "fallback.h" // IWYU pragma: keep -#include "color.h" -/** - Constants for various colors as used by the set_color function. -*/ -enum -{ +/// Constants for various colors as used by the set_color function. +enum { FISH_COLOR_BLACK, FISH_COLOR_RED, FISH_COLOR_GREEN, @@ -32,100 +27,72 @@ enum FISH_COLOR_RESET }; -/** - Sets the fg and bg color. May be called as often as you like, since - if the new color is the same as the previous, nothing will be - written. Negative values for set_color will also be ignored. Since - the terminfo string this function emits can potentially cause the - screen to flicker, the function takes care to write as little as - possible. - - Possible values for color are any form the FISH_COLOR_* enum - and FISH_COLOR_RESET. FISH_COLOR_RESET will perform an - exit_attribute_mode, even if set_color thinks it is already in - FISH_COLOR_NORMAL mode. - - In order to set the color to normal, three terminfo strings may - have to be written. - - - First a string to set the color, such as set_a_foreground. This - is needed because otherwise the previous strings colors might be - removed as well. - - - After that we write the exit_attribute_mode string to reset all - color attributes. - - - Lastly we may need to write set_a_background or set_a_foreground - to set the other half of the color pair to what it should be. - - \param c Foreground color. - \param c2 Background color. -*/ - - +/// Sets the fg and bg color. May be called as often as you like, since if the new color is the same +/// as the previous, nothing will be written. Negative values for set_color will also be ignored. +/// Since the terminfo string this function emits can potentially cause the screen to flicker, the +/// function takes care to write as little as possible. +/// +/// Possible values for color are any form the FISH_COLOR_* enum and FISH_COLOR_RESET. +/// FISH_COLOR_RESET will perform an exit_attribute_mode, even if set_color thinks it is already in +/// FISH_COLOR_NORMAL mode. +/// +/// In order to set the color to normal, three terminfo strings may have to be written. +/// +/// - First a string to set the color, such as set_a_foreground. This is needed because otherwise +/// the previous strings colors might be removed as well. +/// +/// - After that we write the exit_attribute_mode string to reset all color attributes. +/// +/// - Lastly we may need to write set_a_background or set_a_foreground to set the other half of the +/// color pair to what it should be. +/// +/// \param c Foreground color. +/// \param c2 Background color. void set_color(rgb_color_t c, rgb_color_t c2); -/** - Write specified multibyte string - */ +/// Write specified multibyte string. void writembs_check(char *mbs, const char *mbs_name, const char *file, long line); #define writembs(mbs) writembs_check((mbs), #mbs, __FILE__, __LINE__) -/** - Write a wide character using the output method specified using output_set_writer(). -*/ +/// Write a wide character using the output method specified using output_set_writer(). int writech(wint_t ch); -/** - Write a wide character string to FD 1. -*/ +/// Write a wide character string to FD 1. void writestr(const wchar_t *str); -/** - Return the internal color code representing the specified color -*/ +/// Return the internal color code representing the specified color. 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. -*/ +/// 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); -/** - Set the function used for writing in move_cursor, writespace and - set_color and all other output functions in this library. By - default, the write call is used to give completely unbuffered - output to stdout. -*/ +/// Set the function used for writing in move_cursor, writespace and set_color and all other output +/// functions in this library. By default, the write call is used to give completely unbuffered +/// output to stdout. void output_set_writer(int (*writer)(char)); -/** - Return the current output writer - */ -int (*output_get_writer())(char) ; +/// Return the current output writer. +int (*output_get_writer())(char); -/** Set the terminal name */ +/// Set the terminal name. void output_set_term(const wcstring &term); -/** Return the terminal name */ +/// Return the terminal name. const wchar_t *output_get_term(); -/** Sets what colors are supported */ -enum -{ - color_support_term256 = 1 << 0, - color_support_term24bit = 1 << 1 -}; +/// Sets what colors are supported. +enum { color_support_term256 = 1 << 0, color_support_term24bit = 1 << 1 }; typedef unsigned int color_support_t; color_support_t output_get_color_support(); void output_set_color_support(color_support_t support); -/** Given a list of rgb_color_t, pick the "best" one, as determined by the color support. Returns rgb_color_t::none() if empty */ +/// Given a list of rgb_color_t, pick the "best" one, as determined by the color support. Returns +/// rgb_color_t::none() if empty. rgb_color_t best_color(const std::vector &colors, color_support_t support); -/* Exported for builtin_set_color's usage only */ +// Exported for builtin_set_color's usage only. void write_color(rgb_color_t color, bool is_fg); unsigned char index_for_color(rgb_color_t c); From 13d7432368dbb4272997b3b4cb7d50cc5e9f786b Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Mon, 2 May 2016 11:54:01 -0700 Subject: [PATCH 196/363] restyle pager & lru module to match project style Reduces lint errors from 65 to 25 (-63%). Line count from 1439 to 1218 (-15%). Another step in resolving issue #2902. --- src/lru.h | 239 +++++-------- src/pager.cpp | 927 +++++++++++++++++++++----------------------------- src/pager.h | 137 ++++---- 3 files changed, 541 insertions(+), 762 deletions(-) diff --git a/src/lru.h b/src/lru.h index 3f4181e9f..d8a692c2e 100644 --- a/src/lru.h +++ b/src/lru.h @@ -1,257 +1,198 @@ -/** \file lru.h - - Least-recently-used cache implementation -*/ +// Least-recently-used cache implementation. #ifndef FISH_LRU_H #define FISH_LRU_H #include #include +#include #include #include -#include #include "common.h" -/** A predicate to compare dereferenced pointers */ -struct dereference_less_t -{ +/// A predicate to compare dereferenced pointers. +struct dereference_less_t { template - bool operator()(ptr_t p1, ptr_t p2) const - { + bool operator()(ptr_t p1, ptr_t p2) const { return *p1 < *p2; } }; -class lru_node_t -{ - template friend class lru_cache_t; +class lru_node_t { + template + friend class lru_cache_t; - /** Our linked list pointer */ + /// Our linked list pointer. lru_node_t *prev, *next; -public: - /** The key used to look up in the cache */ + public: + /// The key used to look up in the cache. const wcstring key; - /** Constructor */ - explicit lru_node_t(const wcstring &pkey) : prev(NULL), next(NULL), key(pkey) { } + /// Constructor. + explicit lru_node_t(const wcstring &pkey) : prev(NULL), next(NULL), key(pkey) {} - /** Virtual destructor that does nothing for classes that inherit lru_node_t */ + /// Virtual destructor that does nothing for classes that inherit lru_node_t. virtual ~lru_node_t() {} - /** operator< for std::set */ - bool operator<(const lru_node_t &other) const - { - return key < other.key; - } + /// operator< for std::set + bool operator<(const lru_node_t &other) const { return key < other.key; } }; -template -class lru_cache_t -{ -private: - - /** Max node count. This may be (transiently) exceeded by add_node_without_eviction, which is used from background threads. */ +template +class lru_cache_t { + private: + /// Max node count. This may be (transiently) exceeded by add_node_without_eviction, which is + /// used from background threads. const size_t max_node_count; - /** Count of nodes */ + /// Count of nodes. size_t node_count; - /** The set of nodes */ + /// The set of nodes. typedef std::set node_set_t; node_set_t node_set; - void promote_node(node_type_t *node) - { - /* We should never promote the mouth */ + void promote_node(node_type_t *node) { + // We should never promote the mouth. assert(node != &mouth); - /* First unhook us */ + // First unhook us. node->prev->next = node->next; node->next->prev = node->prev; - /* Put us after the mouth */ + // Put us after the mouth. node->next = mouth.next; node->next->prev = node; node->prev = &mouth; mouth.next = node; } - void evict_node(node_type_t *condemned_node) - { - /* We should never evict the mouth */ + void evict_node(node_type_t *condemned_node) { + // We should never evict the mouth. assert(condemned_node != NULL && condemned_node != &mouth); - /* Remove it from the linked list */ + // Remove it from the linked list. condemned_node->prev->next = condemned_node->next; condemned_node->next->prev = condemned_node->prev; - /* Remove us from the set */ + // Remove us from the set. node_set.erase(condemned_node); node_count--; - /* Tell ourselves */ + // Tell ourselves. this->node_was_evicted(condemned_node); } - void evict_last_node(void) - { - /* Simple */ - evict_node((node_type_t *)mouth.prev); - } + void evict_last_node(void) { evict_node((node_type_t *)mouth.prev); } - static lru_node_t *get_previous(lru_node_t *node) - { - return node->prev; - } + static lru_node_t *get_previous(lru_node_t *node) { return node->prev; } -protected: - - /** Head of the linked list */ + protected: + /// Head of the linked list. lru_node_t mouth; - /** Overridable callback for when a node is evicted */ - virtual void node_was_evicted(node_type_t *node) { } + /// Overridable callback for when a node is evicted. + virtual void node_was_evicted(node_type_t *node) {} -public: - - /** Constructor */ - explicit 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! */ + public: + /// Constructor + explicit lru_cache_t(size_t max_size = 1024) + : max_node_count(max_size), node_count(0), mouth(wcstring()) { + // Hook up the mouth to itself: a one node circularly linked list! mouth.prev = mouth.next = &mouth; } - /** Note that we do not evict nodes in our destructor (even though they typically need to be deleted by their creator). */ - virtual ~lru_cache_t() { } + /// Note that we do not evict nodes in our destructor (even though they typically need to be + /// deleted by their creator). + virtual ~lru_cache_t() {} - - /** Returns the node for a given key, or NULL */ - node_type_t *get_node(const wcstring &key) - { + /// Returns the node for a given key, or NULL. + node_type_t *get_node(const wcstring &key) { node_type_t *result = NULL; - /* Construct a fake node as our key */ + // Construct a fake node as our key. lru_node_t node_key(key); - /* Look for it in the set */ + // Look for it in the set. node_set_t::iterator iter = node_set.find(&node_key); - /* If we found a node, promote and return it */ - if (iter != node_set.end()) - { - result = static_cast(*iter); + // If we found a node, promote and return it. + if (iter != node_set.end()) { + result = static_cast(*iter); promote_node(result); } return result; } - /** Evicts the node for a given key, returning true if a node was evicted. */ - bool evict_node(const wcstring &key) - { - /* Construct a fake node as our key */ + /// Evicts the node for a given key, returning true if a node was evicted. + bool evict_node(const wcstring &key) { + // Construct a fake node as our key. lru_node_t node_key(key); - /* Look for it in the set */ + // Look for it in the set. node_set_t::iterator iter = node_set.find(&node_key); - if (iter == node_set.end()) - return false; + if (iter == node_set.end()) return false; - /* Evict the given node */ - evict_node(static_cast(*iter)); + // Evict the given node. + evict_node(static_cast(*iter)); return true; } - /** Adds a node under the given key. Returns true if the node was added, false if the node was not because a node with that key is already in the set. */ - bool add_node(node_type_t *node) - { - /* Add our node without eviction */ - if (! this->add_node_without_eviction(node)) - return false; + /// Adds a node under the given key. Returns true if the node was added, false if the node was + /// not because a node with that key is already in the set. + bool add_node(node_type_t *node) { + // Add our node without eviction. + if (!this->add_node_without_eviction(node)) return false; - /* Evict */ - while (node_count > max_node_count) - evict_last_node(); - - /* Success */ + while (node_count > max_node_count) evict_last_node(); // evict return true; } - /** Adds a node under the given key without triggering eviction. Returns true if the node was added, false if the node was not because a node with that key is already in the set. */ - bool add_node_without_eviction(node_type_t *node) - { + /// Adds a node under the given key without triggering eviction. Returns true if the node was + /// added, false if the node was not because a node with that key is already in the set. + bool add_node_without_eviction(node_type_t *node) { assert(node != NULL && node != &mouth); - /* Try inserting; return false if it was already in the set */ - if (! node_set.insert(node).second) - return false; + // Try inserting; return false if it was already in the set. + if (!node_set.insert(node).second) return false; - /* Add the node after the mouth */ + // Add the node after the mouth. node->next = mouth.next; node->next->prev = node; node->prev = &mouth; mouth.next = node; - /* Update the count. This may push us over the maximum node count. */ + // Update the count. This may push us over the maximum node count. node_count++; - - /* Success */ return true; } - /** Counts nodes */ - size_t size(void) - { - return node_count; - } + /// Counts nodes. + size_t size(void) { return node_count; } - /** Evicts all nodes */ - void evict_all_nodes(void) - { - while (node_count > 0) - { + /// Evicts all nodes. + void evict_all_nodes(void) { + while (node_count > 0) { evict_last_node(); } } - /** Iterator for walking nodes, from least recently used to most */ - class iterator - { + /// Iterator for walking nodes, from least recently used to most. + class iterator { lru_node_t *node; - public: - explicit iterator(lru_node_t *val) : node(val) { } - void operator++() - { - node = lru_cache_t::get_previous(node); - } - void operator++(int x) - { - node = lru_cache_t::get_previous(node); - } - 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); - } + + public: + explicit iterator(lru_node_t *val) : node(val) {} + void operator++() { node = lru_cache_t::get_previous(node); } + void operator++(int x) { node = lru_cache_t::get_previous(node); } + 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); } }; - #endif diff --git a/src/pager.cpp b/src/pager.cpp index 1fb7847ed..446e0704e 100644 --- a/src/pager.cpp +++ b/src/pager.cpp @@ -1,86 +1,72 @@ // IWYU pragma: no_include #include +#include #include #include -#include #include -#include +#include +#include "common.h" +#include "complete.h" +#include "highlight.h" +#include "pager.h" +#include "reader.h" +#include "screen.h" #include "util.h" #include "wutil.h" // IWYU pragma: keep -#include "pager.h" -#include "highlight.h" -#include "common.h" -#include "screen.h" -#include "complete.h" -#include "reader.h" typedef pager_t::comp_t comp_t; typedef std::vector completion_list_t; typedef std::vector comp_info_list_t; -/** The minimum width (in characters) the terminal must to show completions at all */ +/// The minimum width (in characters) the terminal must to show completions at all. #define PAGER_MIN_WIDTH 16 -/** The maximum number of columns of completion to attempt to fit onto the screen */ +/// The maximum number of columns of completion to attempt to fit onto the screen. #define PAGER_MAX_COLS 6 -/** Width of the search field */ +/// Width of the search field. #define PAGER_SEARCH_FIELD_WIDTH 12 -/** Text we use for the search field */ +/// Text we use for the search field. #define SEARCH_FIELD_PROMPT _(L"search: ") -/* Returns numer / denom, rounding up. As a "courtesy" 0/0 is 0. */ -static size_t divide_round_up(size_t numer, size_t denom) -{ - if (numer == 0) - return 0; +/// Returns numer / denom, rounding up. As a "courtesy" 0/0 is 0. +static size_t divide_round_up(size_t numer, size_t denom) { + if (numer == 0) return 0; assert(denom > 0); bool has_rem = (numer % denom) > 0; return numer / denom + (has_rem ? 1 : 0); } -/** - This function calculates the minimum width for each completion - entry in the specified array_list. This width depends on the - terminal size, so this function should be called when the terminal - changes size. -*/ -void pager_t::recalc_min_widths(comp_info_list_t * lst) const -{ - for (size_t i=0; isize(); i++) - { +/// This function calculates the minimum width for each completion entry in the specified +/// array_list. This width depends on the terminal size, so this function should be called when the +/// terminal changes size. +void pager_t::recalc_min_widths(comp_info_list_t *lst) const { + for (size_t i = 0; i < lst->size(); i++) { comp_t *c = &lst->at(i); - - c->min_width = mini(c->desc_width, maxi(0, available_term_width/3 - 2)) + - mini(c->desc_width, maxi(0, available_term_width/5 - 4)) +4; + c->min_width = mini(c->desc_width, maxi(0, available_term_width / 3 - 2)) + + mini(c->desc_width, maxi(0, available_term_width / 5 - 4)) + 4; } - } -/** - Print the specified string, but use at most the specified amount of - space. If the whole string can't be fitted, ellipsize it. - - \param str the string to print - \param color the color to apply to every printed character - \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 wcstring &str, highlight_spec_t color, int max, bool has_more, line_t *line) -{ +/// Print the specified string, but use at most the specified amount of space. If the whole string +/// can't be fitted, ellipsize it. +/// +/// \param str the string to print +/// \param color the color to apply to every printed character +/// \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 wcstring &str, highlight_spec_t color, int max, bool has_more, + line_t *line) { int written = 0; - for (size_t i=0; i < str.size(); i++) - { + for (size_t i = 0; i < str.size(); i++) { wchar_t c = str.at(i); - if (written + wcwidth(c) > max) - break; - if ((written + wcwidth(c) == max) && (has_more || i + 1 < str.size())) - { + if (written + wcwidth(c) > max) break; + if ((written + wcwidth(c) == max) && (has_more || i + 1 < str.size())) { line->append(ellipsis_char, color); written += wcwidth(ellipsis_char); break; @@ -92,76 +78,59 @@ static int print_max(const wcstring &str, highlight_spec_t color, int max, bool return written; } - -/** - Print the specified item using at the specified amount of space -*/ -line_t pager_t::completion_print_item(const wcstring &prefix, const comp_t *c, size_t row, size_t column, int width, bool secondary, bool selected, page_rendering_t *rendering) const -{ - int comp_width=0, desc_width=0; - int written=0; +/// Print the specified item using at the specified amount of space. +line_t pager_t::completion_print_item(const wcstring &prefix, const comp_t *c, size_t row, + size_t column, int width, bool secondary, bool selected, + page_rendering_t *rendering) const { + int comp_width = 0, desc_width = 0; + int written = 0; line_t line_data; - if (c->pref_width <= width) - { - /* - The entry fits, we give it as much space as it wants - */ + if (c->pref_width <= width) { + // The entry fits, we give it as much space as it wants. comp_width = c->comp_width; desc_width = c->desc_width; - } - else - { - /* - The completion and description won't fit on the - allocated space. Give a maximum of 2/3 of the - space to the completion, and whatever is left to - the description. - */ - int desc_all = c->desc_width?c->desc_width+4:0; - - comp_width = maxi(mini(c->comp_width, 2*(width-4)/3), width - desc_all); - if (c->desc_width) - desc_width = width-comp_width-4; + } else { + // 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; } int bg_color = secondary ? highlight_spec_pager_secondary : highlight_spec_normal; - if (selected) - { + if (selected) { bg_color = highlight_spec_search_match; } - for (size_t i=0; icomp.size(); i++) - { + for (size_t i = 0; i < c->comp.size(); i++) { const wcstring &comp = c->comp.at(i); if (i != 0) - written += print_max(PAGER_SPACER_STRING, highlight_spec_normal, comp_width - written, true /* has_more */, &line_data); + written += print_max(PAGER_SPACER_STRING, highlight_spec_normal, comp_width - written, + true /* has_more */, &line_data); int packed_color = highlight_spec_pager_prefix | highlight_make_background(bg_color); - written += print_max(prefix, packed_color, comp_width - written, ! comp.empty(), &line_data); + written += print_max(prefix, packed_color, comp_width - written, !comp.empty(), &line_data); packed_color = highlight_spec_pager_completion | highlight_make_background(bg_color); - written += print_max(comp, packed_color, comp_width - written, i + 1 < c->comp.size(), &line_data); + written += + print_max(comp, packed_color, comp_width - written, i + 1 < c->comp.size(), &line_data); } - if (desc_width) - { + if (desc_width) { int packed_color = highlight_spec_pager_description | highlight_make_background(bg_color); - while (written < (width-desc_width-2)) //the 2 here refers to the parenthesis below + while (written < (width - desc_width - 2)) // the 2 here refers to the parenthesis below { written += print_max(L" ", packed_color, 1, false, &line_data); } written += print_max(L"(", packed_color, 1, false, &line_data); written += print_max(c->desc, packed_color, desc_width, false, &line_data); written += print_max(L")", packed_color, 1, false, &line_data); - } - else - { - while (written < width) - { + } else { + while (written < width) { written += print_max(L" ", 0, 1, false, &line_data); } } @@ -169,259 +138,217 @@ line_t pager_t::completion_print_item(const wcstring &prefix, const comp_t *c, s return line_data; } -/** - Print the specified part of the completion list, using the - specified column offsets and quoting style. - - \param l The list of completions to print - \param cols number of columns to print in - \param width An array specifying the width of each column - \param row_start The first row to print - \param row_stop the row after the last row to print - \param prefix The string to print before each completion -*/ - -void pager_t::completion_print(size_t cols, int *width_per_column, size_t row_start, size_t row_stop, const wcstring &prefix, const comp_info_list_t &lst, page_rendering_t *rendering) const -{ - /* Teach the rendering about the rows it printed */ +/// Print the specified part of the completion list, using the specified column offsets and quoting +/// style. +/// +/// \param l The list of completions to print +/// \param cols number of columns to print in +/// \param width An array specifying the width of each column +/// \param row_start The first row to print +/// \param row_stop the row after the last row to print +/// \param prefix The string to print before each completion +void pager_t::completion_print(size_t cols, int *width_per_column, size_t row_start, + size_t row_stop, const wcstring &prefix, const comp_info_list_t &lst, + page_rendering_t *rendering) const { + // Teach the rendering about the rows it printed. assert(row_start >= 0); assert(row_stop >= row_start); rendering->row_start = row_start; rendering->row_end = row_stop; - size_t rows = (lst.size()-1)/cols+1; + size_t rows = (lst.size() - 1) / cols + 1; size_t effective_selected_idx = this->visual_selected_completion_index(rows, cols); - for (size_t row = row_start; row < row_stop; row++) - { - for (size_t col = 0; col < cols; col++) - { - int is_last = (col==(cols-1)); + for (size_t row = row_start; row < row_stop; row++) { + for (size_t col = 0; col < cols; col++) { + int is_last = (col == (cols - 1)); - if (lst.size() <= col * rows + row) - continue; + if (lst.size() <= col * rows + row) continue; size_t idx = col * rows + row; const comp_t *el = &lst.at(idx); bool is_selected = (idx == effective_selected_idx); - /* Print this completion on its own "line" */ - line_t line = completion_print_item(prefix, el, row, col, width_per_column[col] - (is_last ? 0 : PAGER_SPACER_STRING_WIDTH), row%2, is_selected, rendering); + // Print this completion on its own "line". + line_t line = completion_print_item( + prefix, el, row, col, + width_per_column[col] - (is_last ? 0 : PAGER_SPACER_STRING_WIDTH), row % 2, + is_selected, rendering); - /* If there's more to come, append two spaces */ - if (col + 1 < cols) - { + // If there's more to come, append two spaces. + if (col + 1 < cols) { line.append(PAGER_SPACER_STRING, 0); } - /* Append this to the real line */ + // Append this to the real line. rendering->screen_data.create_line(row - row_start).append_line(line); } } } - -/* Trim leading and trailing whitespace, and compress other whitespace runs into a single space. */ -static void mangle_1_completion_description(wcstring *str) -{ +/// Trim leading and trailing whitespace, and compress other whitespace runs into a single space. +static void mangle_1_completion_description(wcstring *str) { size_t leading = 0, trailing = 0, len = str->size(); - // Skip leading spaces - for (; leading < len; leading++) - { - if (! iswspace(str->at(leading))) - break; + // Skip leading spaces. + for (; leading < len; leading++) { + if (!iswspace(str->at(leading))) break; } - // Compress runs of spaces to a single space + // Compress runs of spaces to a single space. bool was_space = false; - for (; leading < len; leading++) - { + for (; leading < len; leading++) { wchar_t wc = str->at(leading); bool is_space = iswspace(wc); - if (! is_space) - { - // normal character + if (!is_space) { // normal character str->at(trailing++) = wc; - } - else if (! was_space) - { - // initial space in a run + } else if (!was_space) { // initial space in a run str->at(trailing++) = L' '; - } - else - { - // non-initial space in a run, do nothing + } else { // non-initial space in a run, do nothing } was_space = is_space; } - // leading is now at len, trailing is the new length of the string - // Delete trailing spaces - while (trailing > 0 && iswspace(str->at(trailing - 1))) - { + // leading is now at len, trailing is the new length of the string. Delete trailing spaces. + while (trailing > 0 && iswspace(str->at(trailing - 1))) { trailing--; } str->resize(trailing); } -static void join_completions(comp_info_list_t *comps) -{ - // A map from description to index in the completion list of the element with that description - // The indexes are stored +1 +static void join_completions(comp_info_list_t *comps) { + // A map from description to index in the completion list of the element with that description. + // The indexes are stored +1. std::map desc_table; - // note that we mutate the completion list as we go, so the size changes - for (size_t i=0; i < comps->size(); i++) - { + // Note that we mutate the completion list as we go, so the size changes. + for (size_t i = 0; i < comps->size(); i++) { const comp_t &new_comp = comps->at(i); const wcstring &desc = new_comp.desc; - if (desc.empty()) - continue; + if (desc.empty()) continue; - // See if it's in the table + // See if it's in the table. size_t prev_idx_plus_one = desc_table[desc]; - if (prev_idx_plus_one == 0) - { - // We're the first with this description - desc_table[desc] = i+1; - } - else - { + if (prev_idx_plus_one == 0) { + // We're the first with this description. + desc_table[desc] = i + 1; + } else { // There's a prior completion with this description. Append the new ones to it. comp_t *prior_comp = &comps->at(prev_idx_plus_one - 1); - prior_comp->comp.insert(prior_comp->comp.end(), new_comp.comp.begin(), new_comp.comp.end()); + prior_comp->comp.insert(prior_comp->comp.end(), new_comp.comp.begin(), + new_comp.comp.end()); - // Erase the element at this index, and decrement the index to reflect that fact + // Erase the element at this index, and decrement the index to reflect that fact. comps->erase(comps->begin() + i); i -= 1; } } } -/** Generate a list of comp_t structures from a list of completions */ -static comp_info_list_t process_completions_into_infos(const completion_list_t &lst, const wcstring &prefix) -{ +/// Generate a list of comp_t structures from a list of completions. +static comp_info_list_t process_completions_into_infos(const completion_list_t &lst, + const wcstring &prefix) { const size_t lst_size = lst.size(); - // Make the list of the correct size up-front + // Make the list of the correct size up-front. comp_info_list_t result(lst_size); - for (size_t i=0; icomp.push_back(escape_string(comp.completion, ESCAPE_ALL | ESCAPE_NO_QUOTED)); - // Append the mangled description + // Append the mangled description. comp_info->desc = comp.description; mangle_1_completion_description(&comp_info->desc); - // Set the representative completion + // Set the representative completion. comp_info->representative = comp; } return result; } -void pager_t::measure_completion_infos(comp_info_list_t *infos, const wcstring &prefix) const -{ +void pager_t::measure_completion_infos(comp_info_list_t *infos, const wcstring &prefix) const { size_t prefix_len = fish_wcswidth(prefix.c_str()); - for (size_t i=0; i < infos->size(); i++) - { + for (size_t i = 0; i < infos->size(); i++) { comp_t *comp = &infos->at(i); - // Compute comp_width + // Compute comp_width. const wcstring_list_t &comp_strings = comp->comp; - for (size_t j=0; j < comp_strings.size(); j++) - { - // If there's more than one, append the length of ', ' - if (j >= 1) - comp->comp_width += 2; + for (size_t j = 0; j < comp_strings.size(); j++) { + // If there's more than one, append the length of ', '. + if (j >= 1) comp->comp_width += 2; comp->comp_width += prefix_len + fish_wcswidth(comp_strings.at(j).c_str()); } - // Compute desc_width + // Compute desc_width. comp->desc_width = fish_wcswidth(comp->desc.c_str()); - // Compute preferred width - comp->pref_width = comp->comp_width + comp->desc_width + (comp->desc_width?4:0); + // Compute preferred width. + comp->pref_width = comp->comp_width + comp->desc_width + (comp->desc_width ? 4 : 0); } recalc_min_widths(infos); } -/* Indicates if the given completion info passes any filtering we have */ -bool pager_t::completion_info_passes_filter(const comp_t &info) const -{ - /* If we have no filter, everything passes */ - if (! search_field_shown || this->search_field_line.empty()) - return true; +// Indicates if the given completion info passes any filtering we have. +bool pager_t::completion_info_passes_filter(const comp_t &info) const { + // If we have no filter, everything passes. + if (!search_field_shown || this->search_field_line.empty()) return true; const wcstring &needle = this->search_field_line.text; - /* We do substring matching */ + // We do substring matching. const fuzzy_match_type_t limit = fuzzy_match_substring; - /* Match against the description */ - if (string_fuzzy_match_string(needle, info.desc, limit).type != fuzzy_match_none) - { + // Match against the description. + if (string_fuzzy_match_string(needle, info.desc, limit).type != fuzzy_match_none) { return true; } - /* Match against the completion strings */ - for (size_t i=0; i < info.comp.size(); i++) - { - if (string_fuzzy_match_string(needle, prefix + info.comp.at(i), limit).type != fuzzy_match_none) - { + // Match against the completion strings. + for (size_t i = 0; i < info.comp.size(); i++) { + if (string_fuzzy_match_string(needle, prefix + info.comp.at(i), limit).type != + fuzzy_match_none) { return true; } } - /* No match */ - return false; + return false; // no match } -/* Update completion_infos from unfiltered_completion_infos, to reflect the filter */ -void pager_t::refilter_completions() -{ +// Update completion_infos from unfiltered_completion_infos, to reflect the filter. +void pager_t::refilter_completions() { this->completion_infos.clear(); - for (size_t i=0; i < this->unfiltered_completion_infos.size(); i++) - { + for (size_t i = 0; i < this->unfiltered_completion_infos.size(); i++) { const comp_t &info = this->unfiltered_completion_infos.at(i); - if (this->completion_info_passes_filter(info)) - { + if (this->completion_info_passes_filter(info)) { this->completion_infos.push_back(info); } } } -void pager_t::set_completions(const completion_list_t &raw_completions) -{ - // Get completion infos out of it +void pager_t::set_completions(const completion_list_t &raw_completions) { + // Get completion infos out of it. unfiltered_completion_infos = process_completions_into_infos(raw_completions, prefix); - // Maybe join them - if (prefix == L"-") - join_completions(&unfiltered_completion_infos); + // Maybe join them. + if (prefix == L"-") join_completions(&unfiltered_completion_infos); - // Compute their various widths + // Compute their various widths. measure_completion_infos(&unfiltered_completion_infos, prefix); - // Refilter them + // Refilter them. this->refilter_completions(); } -void pager_t::set_prefix(const wcstring &pref) -{ - prefix = pref; -} +void pager_t::set_prefix(const wcstring &pref) { prefix = pref; } -void pager_t::set_term_size(int w, int h) -{ +void pager_t::set_term_size(int w, int h) { assert(w > 0); assert(h > 0); available_term_width = w; @@ -429,124 +356,98 @@ void pager_t::set_term_size(int w, int h) recalc_min_widths(&completion_infos); } -/** - Try to print the list of completions l with the prefix prefix using - cols as the number of columns. Return true if the completion list was - printed, false if the terminal is to narrow for the specified number of - columns. Always succeeds if cols is 1. -*/ - -bool pager_t::completion_try_print(size_t cols, const wcstring &prefix, const comp_info_list_t &lst, page_rendering_t *rendering, size_t suggested_start_row) const -{ - /* - The calculated preferred width of each column - */ +/// Try to print the list of completions l with the prefix prefix using cols as the number of +/// columns. Return true if the completion list was printed, false if the terminal is to narrow for +/// the specified number of columns. Always succeeds if cols is 1. +bool pager_t::completion_try_print(size_t cols, const wcstring &prefix, const comp_info_list_t &lst, + page_rendering_t *rendering, size_t suggested_start_row) const { + // The calculated preferred width of each column. int pref_width[PAGER_MAX_COLS] = {0}; - /* - The calculated minimum width of each column - */ + // The calculated minimum width of each column. int min_width[PAGER_MAX_COLS] = {0}; - /* - If the list can be printed with this width, width will contain the width of each column - */ - int *width=pref_width; + // 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 */ + // Set to one if the list should be printed at this width. bool print = false; - /* Compute the effective term width and term height, accounting for disclosure */ + // Compute the effective term width and term height, accounting for disclosure. int term_width = this->available_term_width; - int term_height = this->available_term_height - 1 - (search_field_shown ? 1 : 0); // we always subtract 1 to make room for a comment row - if (! this->fully_disclosed) - { + int term_height = + this->available_term_height - 1 - + (search_field_shown ? 1 : 0); // we always subtract 1 to make room for a comment row + if (!this->fully_disclosed) { term_height = mini(term_height, PAGER_UNDISCLOSED_MAX_ROWS); } size_t row_count = divide_round_up(lst.size(), cols); - /* We have more to disclose if we are not fully disclosed and there's more rows than we have in our term height */ - if (! this->fully_disclosed && row_count > term_height) - { + // We have more to disclose if we are not fully disclosed and there's more rows than we have in + // our term height. + if (!this->fully_disclosed && row_count > term_height) { rendering->remaining_to_disclose = row_count - term_height; - } - else - { + } else { rendering->remaining_to_disclose = 0; } - - /* If we have only one row remaining to disclose, then squelch the comment row. This prevents us from consuming a line to show "...and 1 more row" */ - if (! this->fully_disclosed && rendering->remaining_to_disclose == 1) - { + + // If we have only one row remaining to disclose, then squelch the comment row. This prevents us + // from consuming a line to show "...and 1 more row". + if (!this->fully_disclosed && rendering->remaining_to_disclose == 1) { term_height += 1; rendering->remaining_to_disclose = 0; } - int pref_tot_width=0; + int pref_tot_width = 0; int min_tot_width = 0; - /* Skip completions on tiny terminals */ - if (term_width < PAGER_MIN_WIDTH) - return true; + // Skip completions on tiny terminals. + if (term_width < PAGER_MIN_WIDTH) return true; - /* Calculate how wide the list would be */ - for (long col = 0; col < cols; col++) - { - for (long row = 0; rowpref_width; min = c->min_width; - if (col != cols-1) - { + if (col != cols - 1) { pref += 2; min += 2; } - min_width[col] = maxi(min_width[col], - min); - pref_width[col] = maxi(pref_width[col], - pref); + min_width[col] = maxi(min_width[col], min); + pref_width[col] = maxi(pref_width[col], pref); } min_tot_width += min_width[col]; pref_tot_width += pref_width[col]; } - /* - Force fit if one column - */ - if (cols == 1) - { - if (pref_tot_width > term_width) - { + + // Force fit if one column. + if (cols == 1) { + if (pref_tot_width > term_width) { pref_width[0] = term_width; } width = pref_width; print = true; - } - else if (pref_tot_width <= term_width) - { - /* Terminal is wide enough. Print the list! */ + } else if (pref_tot_width <= term_width) { + // Terminal is wide enough. Print the list! width = pref_width; print = true; } - if (print) - { - /* Determine the starting and stop row */ + if (print) { + // Determine the starting and stop row. size_t start_row = 0, stop_row = 0; - if (row_count <= term_height) - { - /* Easy, we can show everything */ + if (row_count <= term_height) { + // Easy, we can show everything. start_row = 0; stop_row = row_count; - } - else - { - /* We can only show part of the full list. Determine which part based on the suggested_start_row */ + } else { + // We can only show part of the full list. Determine which part based on the + // suggested_start_row. assert(row_count > term_height); size_t last_starting_row = row_count - term_height; start_row = mini(suggested_start_row, last_starting_row); @@ -559,153 +460,143 @@ bool pager_t::completion_try_print(size_t cols, const wcstring &prefix, const co assert(stop_row - start_row <= term_height); completion_print(cols, width, start_row, stop_row, prefix, lst, rendering); - /* Ellipsis helper string. Either empty or containing the ellipsis char */ + // Ellipsis helper string. Either empty or containing the ellipsis char. const wchar_t ellipsis_string[] = {ellipsis_char == L'\x2026' ? L'\x2026' : L'\0', L'\0'}; - /* Add the progress line. It's a "more to disclose" line if necessary, or a row listing if it's scrollable; otherwise ignore it */ + // Add the progress line. It's a "more to disclose" line if necessary, or a row listing if + // it's scrollable; otherwise ignore it. wcstring progress_text; - if (rendering->remaining_to_disclose == 1) - { - /* I don't expect this case to ever happen */ + if (rendering->remaining_to_disclose == 1) { + // I don't expect this case to ever happen. progress_text = format_string(_(L"%lsand 1 more row"), ellipsis_string); - } - else if (rendering->remaining_to_disclose > 1) - { - progress_text = format_string(_(L"%lsand %lu more rows"), ellipsis_string, (unsigned long)rendering->remaining_to_disclose); - } - else if (start_row > 0 || stop_row < row_count) - { - /* We have a scrollable interface. The +1 here is because we are zero indexed, but want to present things as 1-indexed. We do not add 1 to stop_row or row_count because these are the "past the last value" */ - progress_text = format_string(_(L"rows %lu to %lu of %lu"), start_row + 1, stop_row, row_count); - } - else if (completion_infos.empty() && ! unfiltered_completion_infos.empty()) - { - /* Everything is filtered */ + } else if (rendering->remaining_to_disclose > 1) { + progress_text = format_string(_(L"%lsand %lu more rows"), ellipsis_string, + (unsigned long)rendering->remaining_to_disclose); + } else if (start_row > 0 || stop_row < row_count) { + // We have a scrollable interface. The +1 here is because we are zero indexed, but want + // to present things as 1-indexed. We do not add 1 to stop_row or row_count because + // these are the "past the last value". + progress_text = + format_string(_(L"rows %lu to %lu of %lu"), start_row + 1, stop_row, row_count); + } else if (completion_infos.empty() && !unfiltered_completion_infos.empty()) { + // Everything is filtered. progress_text = _(L"(no matches)"); } - if (! progress_text.empty()) - { + if (!progress_text.empty()) { line_t &line = rendering->screen_data.add_line(); - print_max(progress_text, highlight_spec_pager_progress | highlight_make_background(highlight_spec_pager_progress), term_width, true /* has_more */, &line); + print_max(progress_text, highlight_spec_pager_progress | + highlight_make_background(highlight_spec_pager_progress), + term_width, true /* has_more */, &line); } - if (search_field_shown) - { - /* Add the search field */ + if (search_field_shown) { + // Add the search field. wcstring search_field_text = search_field_line.text; - /* Append spaces to make it at least the required width */ - if (search_field_text.size() < PAGER_SEARCH_FIELD_WIDTH) - { + // Append spaces to make it at least the required width. + if (search_field_text.size() < PAGER_SEARCH_FIELD_WIDTH) { search_field_text.append(PAGER_SEARCH_FIELD_WIDTH - search_field_text.size(), L' '); } line_t *search_field = &rendering->screen_data.insert_line_at_index(0); - /* We limit the width to term_width - 1 */ - int search_field_written = print_max(SEARCH_FIELD_PROMPT, highlight_spec_normal, term_width - 1, false, search_field); - search_field_written += print_max(search_field_text, highlight_modifier_force_underline, term_width - search_field_written - 1, false, search_field); + // We limit the width to term_width - 1. + int search_field_written = print_max(SEARCH_FIELD_PROMPT, highlight_spec_normal, + term_width - 1, false, search_field); + search_field_written += + print_max(search_field_text, highlight_modifier_force_underline, + term_width - search_field_written - 1, false, search_field); } - } return print; } - -page_rendering_t pager_t::render() const -{ - - /** - 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. - */ +page_rendering_t pager_t::render() const { + /// 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. page_rendering_t rendering; rendering.term_width = this->available_term_width; rendering.term_height = this->available_term_height; rendering.search_field_shown = this->search_field_shown; rendering.search_field_line = this->search_field_line; - for (int cols = PAGER_MAX_COLS; cols > 0; cols--) - { - /* Initially empty rendering */ + for (int cols = PAGER_MAX_COLS; cols > 0; cols--) { + // Initially empty rendering. rendering.screen_data.resize(0); - /* Determine how many rows we would need if we had 'cols' columns. Then determine how many columns we want from that. For example, say we had 19 completions. We can fit them into 6 columns, 4 rows, with the last row containing only 1 entry. Or we can fit them into 5 columns, 4 rows, the last row containing 4 entries. Since fewer columns with the same number of rows is better, skip cases where we know we can do better. */ + // Determine how many rows we would need if we had 'cols' columns. Then determine how many + // columns we want from that. For example, say we had 19 completions. We can fit them into 6 + // columns, 4 rows, with the last row containing only 1 entry. Or we can fit them into 5 + // columns, 4 rows, the last row containing 4 entries. Since fewer columns with the same + // number of rows is better, skip cases where we know we can do better. size_t min_rows_required_for_cols = divide_round_up(completion_infos.size(), cols); - size_t min_cols_required_for_rows = divide_round_up(completion_infos.size(), min_rows_required_for_cols); + size_t min_cols_required_for_rows = + divide_round_up(completion_infos.size(), min_rows_required_for_cols); assert(min_cols_required_for_rows <= cols); - if (cols > 1 && min_cols_required_for_rows < cols) - { - /* Next iteration will be better, so skip this one */ + if (cols > 1 && min_cols_required_for_rows < cols) { + // Next iteration will be better, so skip this one. continue; } rendering.cols = (size_t)cols; rendering.rows = min_rows_required_for_cols; - rendering.selected_completion_idx = this->visual_selected_completion_index(rendering.rows, rendering.cols); + rendering.selected_completion_idx = + this->visual_selected_completion_index(rendering.rows, rendering.cols); - if (completion_try_print(cols, prefix, completion_infos, &rendering, suggested_row_start)) - { + if (completion_try_print(cols, prefix, completion_infos, &rendering, suggested_row_start)) { break; } } return rendering; } -void pager_t::update_rendering(page_rendering_t *rendering) const -{ +void pager_t::update_rendering(page_rendering_t *rendering) const { if (rendering->term_width != this->available_term_width || - rendering->term_height != this->available_term_height || - rendering->selected_completion_idx != this->visual_selected_completion_index(rendering->rows, rendering->cols) || - rendering->search_field_shown != this->search_field_shown || - rendering->search_field_line.text != this->search_field_line.text || - rendering->search_field_line.position != this->search_field_line.position || - (rendering->remaining_to_disclose > 0 && this->fully_disclosed)) - { + rendering->term_height != this->available_term_height || + rendering->selected_completion_idx != + this->visual_selected_completion_index(rendering->rows, rendering->cols) || + rendering->search_field_shown != this->search_field_shown || + rendering->search_field_line.text != this->search_field_line.text || + rendering->search_field_line.position != this->search_field_line.position || + (rendering->remaining_to_disclose > 0 && this->fully_disclosed)) { *rendering = this->render(); } } -pager_t::pager_t() : available_term_width(0), available_term_height(0), selected_completion_idx(PAGER_SELECTION_NONE), suggested_row_start(0), fully_disclosed(false), search_field_shown(false) -{ -} +pager_t::pager_t() + : available_term_width(0), + available_term_height(0), + selected_completion_idx(PAGER_SELECTION_NONE), + suggested_row_start(0), + fully_disclosed(false), + search_field_shown(false) {} -bool pager_t::empty() const -{ - return unfiltered_completion_infos.empty(); -} +bool pager_t::empty() const { return unfiltered_completion_infos.empty(); } -bool pager_t::select_next_completion_in_direction(selection_direction_t direction, const page_rendering_t &rendering) -{ - /* Must have something to select */ - if (this->completion_infos.empty()) - { +bool pager_t::select_next_completion_in_direction(selection_direction_t direction, + const page_rendering_t &rendering) { + // Must have something to select. + if (this->completion_infos.empty()) { return false; } - /* Handle the case of nothing selected yet */ - if (selected_completion_idx == PAGER_SELECTION_NONE) - { - switch (direction) - { - /* These directions do something sane */ + // Handle the case of nothing selected yet. + if (selected_completion_idx == PAGER_SELECTION_NONE) { + switch (direction) { + // These directions do something sane. case direction_south: case direction_page_south: case direction_next: case direction_prev: - if (direction == direction_prev) - { + if (direction == direction_prev) { selected_completion_idx = completion_infos.size() - 1; - } - else - { + } else { selected_completion_idx = 0; } return true; - /* These do nothing */ + // These do nothing. case direction_north: case direction_page_north: case direction_east: @@ -716,80 +607,55 @@ bool pager_t::select_next_completion_in_direction(selection_direction_t directio } } - /* Ok, we had something selected already. Select something different. */ + // Ok, we had something selected already. Select something different. size_t new_selected_completion_idx = selected_completion_idx; - if (! selection_direction_is_cardinal(direction)) - { - /* Next, previous, or deselect, all easy */ - if (direction == direction_deselect) - { + if (!selection_direction_is_cardinal(direction)) { + // Next, previous, or deselect, all easy. + if (direction == direction_deselect) { new_selected_completion_idx = PAGER_SELECTION_NONE; - } - else if (direction == direction_next) - { + } else if (direction == direction_next) { new_selected_completion_idx = selected_completion_idx + 1; - if (new_selected_completion_idx >= completion_infos.size()) - { + if (new_selected_completion_idx >= completion_infos.size()) { new_selected_completion_idx = 0; } - } - else if (direction == direction_prev) - { - if (selected_completion_idx == 0) - { + } else if (direction == direction_prev) { + if (selected_completion_idx == 0) { new_selected_completion_idx = completion_infos.size() - 1; - } - else - { + } else { new_selected_completion_idx = selected_completion_idx - 1; } - } - else - { + } else { assert(0 && "Unknown non-cardinal direction"); } - } - else - { - /* Cardinal directions. We have a completion index; we wish to compute its row and column. */ + } else { + // Cardinal directions. We have a completion index; we wish to compute its row and column. size_t current_row = this->get_selected_row(rendering); size_t current_col = this->get_selected_column(rendering); size_t page_height = maxi(rendering.term_height - 1, 1); - switch (direction) - { - case direction_page_north: - { + switch (direction) { + case direction_page_north: { if (current_row > page_height) - current_row = current_row - page_height; + current_row = current_row - page_height; else - current_row = 0; + current_row = 0; break; } - case direction_north: - { - /* Go up a whole row. If we cycle, go to the previous column. */ - if (current_row > 0) - { + case direction_north: { + // Go up a whole row. If we cycle, go to the previous column. + if (current_row > 0) { current_row--; - } - else - { + } else { current_row = rendering.rows - 1; - if (current_col > 0) - current_col--; + if (current_col > 0) current_col--; } break; } - case direction_page_south: - { - if (current_row + page_height < rendering.rows) - { + case direction_page_south: { + if (current_row + page_height < rendering.rows) { current_row += page_height; - } - else - { + } else { current_row = rendering.rows - 1; if (current_col * rendering.rows + current_row >= completion_infos.size()) { current_row = (completion_infos.size() - 1) % rendering.rows; @@ -797,51 +663,39 @@ bool pager_t::select_next_completion_in_direction(selection_direction_t directio } break; } - case direction_south: - { - /* Go down, unless we are in the last row. Note that this means that we may set selected_completion_idx to an out-of-bounds value if the last row is incomplete; this is a feature (it allows "last column memory"). */ - if (current_row + 1 < rendering.rows) - { + case direction_south: { + // Go down, unless we are in the last row. Note that this means that we may set + // selected_completion_idx to an out-of-bounds value if the last row is incomplete; + // this is a feature (it allows "last column memory"). + if (current_row + 1 < rendering.rows) { current_row++; - } - else - { + } else { current_row = 0; - if (current_col + 1 < rendering.cols) - current_col++; - + if (current_col + 1 < rendering.cols) current_col++; } break; } - case direction_east: - { - /* Go east, wrapping to the next row. There is no "row memory," so if we run off the end, wrap. */ - if (current_col + 1 < rendering.cols && (current_col + 1) * rendering.rows + current_row < completion_infos.size()) - { + case direction_east: { + // Go east, wrapping to the next row. There is no "row memory," so if we run off the + // end, wrap. + if (current_col + 1 < rendering.cols && + (current_col + 1) * rendering.rows + current_row < completion_infos.size()) { current_col++; - } - else - { + } else { current_col = 0; - if (current_row + 1 < rendering.rows) - current_row++; + if (current_row + 1 < rendering.rows) current_row++; } break; } - case direction_west: - { - /* Go west, wrapping to the previous row */ - if (current_col > 0) - { + case direction_west: { + // Go west, wrapping to the previous row. + if (current_col > 0) { current_col--; - } - else - { + } else { current_col = rendering.cols - 1; - if (current_row > 0) - current_row--; + if (current_row > 0) current_row--; } break; } @@ -851,121 +705,106 @@ bool pager_t::select_next_completion_in_direction(selection_direction_t directio break; } - /* Compute the new index based on the changed row */ + // Compute the new index based on the changed row. new_selected_completion_idx = current_col * rendering.rows + current_row; } - if (new_selected_completion_idx != selected_completion_idx) - { + if (new_selected_completion_idx != selected_completion_idx) { selected_completion_idx = new_selected_completion_idx; - /* Update suggested_row_start to ensure the selection is visible. suggested_row_start * rendering.cols is the first suggested visible completion; add the visible completion count to that to get the last one */ + // Update suggested_row_start to ensure the selection is visible. suggested_row_start * + // rendering.cols is the first suggested visible completion; add the visible completion + // count to that to get the last one. size_t visible_row_count = rendering.row_end - rendering.row_start; - if (visible_row_count > 0 && selected_completion_idx != PAGER_SELECTION_NONE) //paranoia + if (visible_row_count > 0 && selected_completion_idx != PAGER_SELECTION_NONE) // paranoia { size_t row_containing_selection = this->get_selected_row(rendering); - /* Ensure our suggested row start is not past the selected row */ - if (suggested_row_start > row_containing_selection) - { + // Ensure our suggested row start is not past the selected row. + if (suggested_row_start > row_containing_selection) { suggested_row_start = row_containing_selection; } - /* Ensure our suggested row start is not too early before it */ - if (suggested_row_start + visible_row_count <= row_containing_selection) - { - /* The user moved south past the bottom completion */ - if (! fully_disclosed && rendering.remaining_to_disclose > 0) - { - /* Perform disclosure */ - fully_disclosed = true; - } - else - { - /* Scroll */ + // Ensure our suggested row start is not too early before it. + if (suggested_row_start + visible_row_count <= row_containing_selection) { + // The user moved south past the bottom completion. + if (!fully_disclosed && rendering.remaining_to_disclose > 0) { + fully_disclosed = true; // perform disclosure + } else { + // Scroll suggested_row_start = row_containing_selection - visible_row_count + 1; - - /* Ensure fully_disclosed is set. I think we can hit this case if the user resizes the window - we don't want to drop back to the disclosed style */ + // Ensure fully_disclosed is set. I think we can hit this case if the user + // resizes the window - we don't want to drop back to the disclosed style. fully_disclosed = true; } } } return true; - } - else - { + } else { return false; } } -size_t pager_t::visual_selected_completion_index(size_t rows, size_t cols) const -{ - /* No completions -> no selection */ - if (completion_infos.empty() || rows == 0 || cols == 0) - { +size_t pager_t::visual_selected_completion_index(size_t rows, size_t cols) const { + // No completions -> no selection. + if (completion_infos.empty() || rows == 0 || cols == 0) { return PAGER_SELECTION_NONE; } size_t result = selected_completion_idx; - if (result != PAGER_SELECTION_NONE) - { - /* If the selected completion is beyond the last selection, go left by columns until it's within it. This is how we implement "column memory." */ - while (result >= completion_infos.size() && result >= rows) - { + if (result != PAGER_SELECTION_NONE) { + // If the selected completion is beyond the last selection, go left by columns until it's + // within it. This is how we implement "column memory". + while (result >= completion_infos.size() && result >= rows) { result -= rows; } - /* If we are still beyond the last selection, clamp it */ - if (result >= completion_infos.size()) - result = completion_infos.size() - 1; + // If we are still beyond the last selection, clamp it. + if (result >= completion_infos.size()) result = completion_infos.size() - 1; } assert(result == PAGER_SELECTION_NONE || result < completion_infos.size()); return result; } -/* It's possible we have no visual selection but are still navigating the contents, e.g. every completion is filtered */ -bool pager_t::is_navigating_contents() const -{ +// It's possible we have no visual selection but are still navigating the contents, e.g. every +// completion is filtered. +bool pager_t::is_navigating_contents() const { return selected_completion_idx != PAGER_SELECTION_NONE; } -void pager_t::set_fully_disclosed(bool flag) -{ - fully_disclosed = flag; -} +void pager_t::set_fully_disclosed(bool flag) { fully_disclosed = flag; } -const completion_t *pager_t::selected_completion(const page_rendering_t &rendering) const -{ - const completion_t * result = NULL; +const completion_t *pager_t::selected_completion(const page_rendering_t &rendering) const { + const completion_t *result = NULL; size_t idx = visual_selected_completion_index(rendering.rows, rendering.cols); - if (idx != PAGER_SELECTION_NONE) - { + if (idx != PAGER_SELECTION_NONE) { result = &completion_infos.at(idx).representative; } return result; } -/* Get the selected row and column. Completions are rendered column first, i.e. we go south before we go west. So if we have N rows, and our selected index is N + 2, then our row is 2 (mod by N) and our column is 1 (divide by N) */ -size_t pager_t::get_selected_row(const page_rendering_t &rendering) const -{ - if (rendering.rows == 0) - return PAGER_SELECTION_NONE; +/// Get the selected row and column. Completions are rendered column first, i.e. we go south before +/// we go west. So if we have N rows, and our selected index is N + 2, then our row is 2 (mod by N) +/// and our column is 1 (divide by N). +size_t pager_t::get_selected_row(const page_rendering_t &rendering) const { + if (rendering.rows == 0) return PAGER_SELECTION_NONE; - return selected_completion_idx == PAGER_SELECTION_NONE ? PAGER_SELECTION_NONE : selected_completion_idx % rendering.rows; + return selected_completion_idx == PAGER_SELECTION_NONE + ? PAGER_SELECTION_NONE + : selected_completion_idx % rendering.rows; } -size_t pager_t::get_selected_column(const page_rendering_t &rendering) const -{ - if (rendering.rows == 0) - return PAGER_SELECTION_NONE; +size_t pager_t::get_selected_column(const page_rendering_t &rendering) const { + if (rendering.rows == 0) return PAGER_SELECTION_NONE; - return selected_completion_idx == PAGER_SELECTION_NONE ? PAGER_SELECTION_NONE : selected_completion_idx / rendering.rows; + return selected_completion_idx == PAGER_SELECTION_NONE + ? PAGER_SELECTION_NONE + : selected_completion_idx / rendering.rows; } -void pager_t::clear() -{ +void pager_t::clear() { unfiltered_completion_infos.clear(); completion_infos.clear(); prefix.clear(); @@ -975,29 +814,27 @@ void pager_t::clear() search_field_line.clear(); } -void pager_t::set_search_field_shown(bool flag) -{ - this->search_field_shown = flag; -} +void pager_t::set_search_field_shown(bool flag) { this->search_field_shown = flag; } -bool pager_t::is_search_field_shown() const -{ - return this->search_field_shown; -} +bool pager_t::is_search_field_shown() const { return this->search_field_shown; } -size_t pager_t::cursor_position() const -{ +size_t pager_t::cursor_position() const { size_t result = wcslen(SEARCH_FIELD_PROMPT) + this->search_field_line.position; - /* Clamp it to the right edge */ - if (available_term_width > 0 && result + 1 > available_term_width) - { + // Clamp it to the right edge. + if (available_term_width > 0 && result + 1 > available_term_width) { result = available_term_width - 1; } return result; } - -/* Constructor */ -page_rendering_t::page_rendering_t() : term_width(-1), term_height(-1), rows(0), cols(0), row_start(0), row_end(0), selected_completion_idx(-1), remaining_to_disclose(0), search_field_shown(false) -{ -} +// Constructor +page_rendering_t::page_rendering_t() + : term_width(-1), + term_height(-1), + rows(0), + cols(0), + row_start(0), + row_end(0), + selected_completion_idx(-1), + remaining_to_disclose(0), + search_field_shown(false) {} diff --git a/src/pager.h b/src/pager.h index c03ca906f..7694398f4 100644 --- a/src/pager.h +++ b/src/pager.h @@ -1,26 +1,23 @@ -/** \file pager.h - Pager support -*/ +// Pager support. #ifndef FISH_PAGER_H #define FISH_PAGER_H +#include #include +#include #include #include -#include -#include #include "common.h" #include "complete.h" -#include "screen.h" #include "reader.h" +#include "screen.h" #define PAGER_SELECTION_NONE ((size_t)(-1)) -/* Represents rendering from the pager */ -class page_rendering_t -{ -public: +/// Represents rendering from the pager. +class page_rendering_t { + public: int term_width; int term_height; size_t rows; @@ -35,144 +32,148 @@ class page_rendering_t bool search_field_shown; editable_line_t search_field_line; - /* Returns a rendering with invalid data, useful to indicate "no rendering" */ + // Returns a rendering with invalid data, useful to indicate "no rendering". page_rendering_t(); }; -/* The space between adjacent completions */ +// The space between adjacent completions. #define PAGER_SPACER_STRING L" " #define PAGER_SPACER_STRING_WIDTH 2 -/* How many rows we will show in the "initial" pager */ +// How many rows we will show in the "initial" pager. #define PAGER_UNDISCLOSED_MAX_ROWS 4 typedef std::vector completion_list_t; -page_rendering_t render_completions(const completion_list_t &raw_completions, const wcstring &prefix); +page_rendering_t render_completions(const completion_list_t &raw_completions, + const wcstring &prefix); -class pager_t -{ +class pager_t { int available_term_width; int available_term_height; size_t selected_completion_idx; size_t suggested_row_start; - /* Fully disclosed means that we show all completions */ + // Fully disclosed means that we show all completions. bool fully_disclosed; - /* Whether we show the search field */ + // Whether we show the search field. bool search_field_shown; - /* Returns the index of the completion that should draw selected, using the given number of columns */ + // Returns the index of the completion that should draw selected, using the given number of + // columns. size_t visual_selected_completion_index(size_t rows, size_t cols) const; - /** Data structure describing one or a group of related completions */ -public: - struct comp_t - { - /** The list of all completin strings this entry applies to */ + public: + /// Data structure describing one or a group of related completions. + struct comp_t { + /// The list of all completin strings this entry applies to. wcstring_list_t comp; - - /** The description */ + /// The description. wcstring desc; - - /** The representative completion */ + /// The representative completion. completion_t representative; - - /** On-screen width of the completion string */ + /// On-screen width of the completion string. int comp_width; - - /** On-screen width of the description information */ + /// On-screen width of the description information. int desc_width; - - /** Preferred total width */ + /// Preferred total width. int pref_width; - - /** Minimum acceptable width */ + /// Minimum acceptable width. int min_width; - comp_t() : comp(), desc(), representative(L""), comp_width(0), desc_width(0), pref_width(0), min_width(0) - { - } + comp_t() + : comp(), + desc(), + representative(L""), + comp_width(0), + desc_width(0), + pref_width(0), + min_width(0) {} }; -private: + private: typedef std::vector comp_info_list_t; - /* The filtered list of completion infos */ + // The filtered list of completion infos. comp_info_list_t completion_infos; - /* The unfiltered list. Note there's a lot of duplication here. */ + // The unfiltered list. Note there's a lot of duplication here. comp_info_list_t unfiltered_completion_infos; wcstring prefix; - bool completion_try_print(size_t cols, const wcstring &prefix, const comp_info_list_t &lst, page_rendering_t *rendering, size_t suggested_start_row) const; + bool completion_try_print(size_t cols, const wcstring &prefix, const comp_info_list_t &lst, + page_rendering_t *rendering, size_t suggested_start_row) const; - void recalc_min_widths(comp_info_list_t * lst) const; + void recalc_min_widths(comp_info_list_t *lst) const; void measure_completion_infos(std::vector *infos, const wcstring &prefix) const; bool completion_info_passes_filter(const comp_t &info) const; - void completion_print(size_t cols, int *width_per_column, size_t row_start, size_t row_stop, const wcstring &prefix, const comp_info_list_t &lst, page_rendering_t *rendering) const; - line_t completion_print_item(const wcstring &prefix, const comp_t *c, size_t row, size_t column, int width, bool secondary, bool selected, page_rendering_t *rendering) const; + void completion_print(size_t cols, int *width_per_column, size_t row_start, size_t row_stop, + const wcstring &prefix, const comp_info_list_t &lst, + page_rendering_t *rendering) const; + line_t completion_print_item(const wcstring &prefix, const comp_t *c, size_t row, size_t column, + int width, bool secondary, bool selected, + page_rendering_t *rendering) const; - -public: - - /* The text of the search field */ + public: + // The text of the search field. editable_line_t search_field_line; - /* Sets the set of completions */ + // Sets the set of completions. void set_completions(const completion_list_t &comp); - /* Sets the prefix */ + // Sets the prefix. void set_prefix(const wcstring &pref); - /* Sets the terminal width and height */ + // Sets the terminal width and height. void set_term_size(int w, int h); - /* Changes the selected completion in the given direction according to the layout of the given rendering. Returns true if the selection changed. */ - bool select_next_completion_in_direction(selection_direction_t direction, const page_rendering_t &rendering); + // Changes the selected completion in the given direction according to the layout of the given + // rendering. Returns true if the selection changed. + bool select_next_completion_in_direction(selection_direction_t direction, + const page_rendering_t &rendering); - /* Returns the currently selected completion for the given rendering */ + // Returns the currently selected completion for the given rendering. const completion_t *selected_completion(const page_rendering_t &rendering) const; - /* Indicates the row and column for the given rendering. Returns -1 if no selection. */ + // Indicates the row and column for the given rendering. Returns -1 if no selection. size_t get_selected_row(const page_rendering_t &rendering) const; size_t get_selected_column(const page_rendering_t &rendering) const; - /* Produces a rendering of the completions, at the given term size */ + // Produces a rendering of the completions, at the given term size. page_rendering_t render() const; - /* Updates the rendering if it's stale */ + // Updates the rendering if it's stale. void update_rendering(page_rendering_t *rendering) const; - /* Indicates if there are no completions, and therefore nothing to render */ + // Indicates if there are no completions, and therefore nothing to render. bool empty() const; - /* Clears all completions and the prefix */ + // Clears all completions and the prefix. void clear(); - /* Updates the completions list per the filter */ + // Updates the completions list per the filter. void refilter_completions(); - /* Sets whether the search field is shown */ + // Sets whether the search field is shown. void set_search_field_shown(bool flag); - /* Gets whether the search field shown */ + // Gets whether the search field shown. bool is_search_field_shown() const; - /* Indicates if we are navigating our contents */ + // Indicates if we are navigating our contents. bool is_navigating_contents() const; - /* Become fully disclosed */ + // Become fully disclosed. void set_fully_disclosed(bool flag); - /* Position of the cursor */ + // Position of the cursor. size_t cursor_position() const; - /* Constructor */ + // Constructor pager_t(); }; From ea945cc4377d91ceeeb0954ae8ad862f82f91918 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Mon, 2 May 2016 12:31:33 -0700 Subject: [PATCH 197/363] restyle parse_execution module to match project style Reduces lint errors from 184 to 84 (-54%). Line count from 2139 to 1943 (-9%). Another step in resolving issue #2902. --- src/parse_constants.h | 324 ++++----- src/parse_execution.cpp | 1381 ++++++++++++++++++--------------------- src/parse_execution.h | 165 ++--- 3 files changed, 837 insertions(+), 1033 deletions(-) diff --git a/src/parse_constants.h b/src/parse_constants.h index 497215045..c43ba7de9 100644 --- a/src/parse_constants.h +++ b/src/parse_constants.h @@ -1,19 +1,19 @@ -/**\file parse_constants.h - - Constants used in the programmatic representation of fish code. -*/ +// Constants used in the programmatic representation of fish code. #ifndef FISH_PARSE_CONSTANTS_H #define FISH_PARSE_CONSTANTS_H #include "config.h" #define PARSE_ASSERT(a) assert(a) -#define PARSER_DIE() do { fprintf(stderr, "Parser dying!\n"); exit_without_destructors(-1); } while (0) +#define PARSER_DIE() \ + do { \ + fprintf(stderr, "Parser dying!\n"); \ + exit_without_destructors(-1); \ + } while (0) // IMPORTANT: If the following enum is modified you must update the corresponding parser_token_types // array in parse_tree.cpp. -enum parse_token_type_t -{ +enum parse_token_type_t { token_type_invalid, // Non-terminal tokens @@ -47,8 +47,8 @@ enum parse_token_type_t symbol_argument_list, - // "freestanding" argument lists are parsed from the argument list supplied to 'complete -a' - // They are not generated by parse trees rooted in symbol_job_list + // Freestanding argument lists are parsed from the argument list supplied to 'complete -a' + // They are not generated by parse trees rooted in symbol_job_list. symbol_freestanding_argument_list, symbol_argument, @@ -58,17 +58,17 @@ enum parse_token_type_t symbol_end_command, - // Terminal types + // Terminal types. parse_token_type_string, parse_token_type_pipe, parse_token_type_redirection, parse_token_type_background, parse_token_type_end, - // Special terminal type that means no more tokens forthcoming + // Special terminal type that means no more tokens forthcoming. parse_token_type_terminate, - // Very special terminal types that don't appear in the production list + // Very special terminal types that don't appear in the production list. parse_special_type_parse_error, parse_special_type_tokenizer_error, parse_special_type_comment, @@ -83,15 +83,14 @@ enum parse_token_type_t LAST_PARSE_TOKEN_TYPE = parse_token_type_end } __packed; // Array of strings corresponding to the enums above instantiated in parse_tree.cpp. -extern const wchar_t * const parser_token_types[]; +extern const wchar_t *const parser_token_types[]; // These must be maintained in sorted order (except for none, which isn't a keyword). This enables // us to do binary search. // // IMPORTANT: If the following enum is modified you must update the corresponding keyword_map array // in parse_tree.cpp. -enum parse_keyword_t -{ +enum parse_keyword_t { parse_keyword_none, parse_keyword_and, parse_keyword_begin, @@ -112,261 +111,206 @@ enum parse_keyword_t LAST_KEYWORD = parse_keyword_while } __packed; -/* Node tag values */ +// Node tag values. -/* Statement decorations, stored in node tag */ -enum parse_statement_decoration_t -{ +// Statement decorations, stored in node tag. +enum parse_statement_decoration_t { parse_statement_decoration_none, parse_statement_decoration_command, parse_statement_decoration_builtin, parse_statement_decoration_exec }; -/* Boolean statement types, stored in node tag */ -enum parse_bool_statement_type_t -{ - parse_bool_and, - parse_bool_or, - parse_bool_not -}; +// Boolean statement types, stored in node tag. +enum parse_bool_statement_type_t { parse_bool_and, parse_bool_or, parse_bool_not }; -/* Whether a statement is backgrounded */ -enum parse_optional_background_t -{ - parse_no_background, - parse_background -}; +// Whether a statement is backgrounded. +enum parse_optional_background_t { parse_no_background, parse_background }; -/* Parse error code list */ -enum parse_error_code_t -{ +// Parse error code list. +enum parse_error_code_t { parse_error_none, - /* Matching values from enum parser_error */ + // Matching values from enum parser_error. parse_error_syntax, parse_error_eval, parse_error_cmdsubst, - parse_error_generic, // unclassified error types + parse_error_generic, // unclassified error types - //tokenizer errors + // Tokenizer errors. parse_error_tokenizer_unterminated_quote, parse_error_tokenizer_unterminated_subshell, parse_error_tokenizer_unterminated_slice, parse_error_tokenizer_unterminated_escape, parse_error_tokenizer_other, - parse_error_unbalancing_end, //end outside of block - parse_error_unbalancing_else, //else outside of if - parse_error_unbalancing_case, //case outside of switch + parse_error_unbalancing_end, // end outside of block + parse_error_unbalancing_else, // else outside of if + parse_error_unbalancing_case, // case outside of switch - parse_error_double_pipe, // foo || bar, has special error message - parse_error_double_background // foo && bar, has special error message + parse_error_double_pipe, // foo || bar, has special error message + parse_error_double_background // foo && bar, has special error message }; -enum -{ - PARSER_TEST_ERROR = 1, - PARSER_TEST_INCOMPLETE = 2 -}; +enum { PARSER_TEST_ERROR = 1, PARSER_TEST_INCOMPLETE = 2 }; typedef unsigned int parser_test_error_bits_t; -struct parse_error_t -{ - /** Text of the error */ +struct parse_error_t { + /// Text of the error. wcstring text; - - /** Code for the error */ + /// Code for the error. enum parse_error_code_t code; - - /** Offset and length of the token in the source code that triggered this error */ + /// Offset and length of the token in the source code that triggered this error. size_t source_start; size_t source_length; - - /** Return a string describing the error, suitable for presentation to the user. If skip_caret is false, the offending line with a caret is printed as well */ + /// Return a string describing the error, suitable for presentation to the user. If skip_caret + /// is false, the offending line with a caret is printed as well. wcstring describe(const wcstring &src) const; - - /** Return a string describing the error, suitable for presentation to the user, with the given prefix. If skip_caret is false, the offending line with a caret is printed as well */ - wcstring describe_with_prefix(const wcstring &src, const wcstring &prefix, bool is_interactive, bool skip_caret) const; + /// Return a string describing the error, suitable for presentation to the user, with the given + /// prefix. If skip_caret is false, the offending line with a caret is printed as well. + wcstring describe_with_prefix(const wcstring &src, const wcstring &prefix, bool is_interactive, + bool skip_caret) const; }; typedef std::vector parse_error_list_t; -/* Special source_start value that means unknown */ +// Special source_start value that means unknown. #define SOURCE_LOCATION_UNKNOWN (static_cast(-1)) -/* Helper function to offset error positions by the given amount. This is used when determining errors in a substring of a larger source buffer. */ +/// Helper function to offset error positions by the given amount. This is used when determining +/// errors in a substring of a larger source buffer. void parse_error_offset_source_start(parse_error_list_t *errors, size_t amt); -/** Maximum number of function calls. */ +/// Maximum number of function calls. #define FISH_MAX_STACK_DEPTH 128 -/** Error message on a function that calls itself immediately */ -#define INFINITE_FUNC_RECURSION_ERR_MSG _( L"The function '%ls' calls itself immediately, which would result in an infinite loop.") +/// Error message on a function that calls itself immediately. +#define INFINITE_FUNC_RECURSION_ERR_MSG \ + _(L"The function '%ls' calls itself immediately, which would result in an infinite loop.") -/** Error message on reaching maximum call stack depth */ -#define CALL_STACK_LIMIT_EXCEEDED_ERR_MSG _( L"The function call stack limit has been exceeded. Do you have an accidental infinite loop?") +/// Error message on reaching maximum call stack depth. +#define CALL_STACK_LIMIT_EXCEEDED_ERR_MSG \ + _(L"The function call stack limit has been exceeded. Do you have an accidental infinite " \ + L"loop?") -/** Error message when encountering an illegal command name */ -#define ILLEGAL_CMD_ERR_MSG _( L"Illegal command name '%ls'") +/// Error message when encountering an illegal command name. +#define ILLEGAL_CMD_ERR_MSG _(L"Illegal command name '%ls'") -/** Error message when encountering an unknown builtin name */ -#define UNKNOWN_BUILTIN_ERR_MSG _( L"Unknown builtin '%ls'") +/// Error message when encountering an unknown builtin name. +#define UNKNOWN_BUILTIN_ERR_MSG _(L"Unknown builtin '%ls'") -/** Error message when encountering a failed expansion, e.g. for the variable name in for loops */ -#define FAILED_EXPANSION_VARIABLE_NAME_ERR_MSG _( L"Unable to expand variable name '%ls'") +/// Error message when encountering a failed expansion, e.g. for the variable name in for loops. +#define FAILED_EXPANSION_VARIABLE_NAME_ERR_MSG _(L"Unable to expand variable name '%ls'") -/** Error message when encountering a failed process expansion, e.g. %notaprocess */ -#define FAILED_EXPANSION_PROCESS_ERR_MSG _( L"Unable to find a process '%ls'") +/// Error message when encountering a failed process expansion, e.g. %notaprocess. +#define FAILED_EXPANSION_PROCESS_ERR_MSG _(L"Unable to find a process '%ls'") -/** Error message when encountering an illegal file descriptor */ -#define ILLEGAL_FD_ERR_MSG _( L"Illegal file descriptor in redirection '%ls'") +/// Error message when encountering an illegal file descriptor. +#define ILLEGAL_FD_ERR_MSG _(L"Illegal file descriptor in redirection '%ls'") -/** Error message for wildcards with no matches */ -#define WILDCARD_ERR_MSG _( L"No matches for wildcard '%ls'.") +/// Error message for wildcards with no matches. +#define WILDCARD_ERR_MSG _(L"No matches for wildcard '%ls'.") -/** Error when using break outside of loop */ -#define INVALID_BREAK_ERR_MSG _( L"'break' while not inside of loop" ) +/// Error when using break outside of loop. +#define INVALID_BREAK_ERR_MSG _(L"'break' while not inside of loop") -/** Error when using continue outside of loop */ -#define INVALID_CONTINUE_ERR_MSG _( L"'continue' while not inside of loop" ) +/// Error when using continue outside of loop. +#define INVALID_CONTINUE_ERR_MSG _(L"'continue' while not inside of loop") -/** Error when using return builtin outside of function definition */ -#define INVALID_RETURN_ERR_MSG _( L"'return' outside of function definition" ) +/// Error when using return builtin outside of function definition. +#define INVALID_RETURN_ERR_MSG _(L"'return' outside of function definition") +// Error messages. The number is a reminder of how many format specifiers are contained. -/*** Error messages. The number is a reminder of how many format specifiers are contained. */ +/// Error for $^. +#define ERROR_BAD_VAR_CHAR1 _(L"$%lc is not a valid variable in fish.") -/** Error for (e.g.) $^ */ -#define ERROR_BAD_VAR_CHAR1 _( L"$%lc is not a valid variable in fish." ) +/// Error for ${a}. +#define ERROR_BRACKETED_VARIABLE1 _(L"Variables cannot be bracketed. In fish, please use {$%ls}.") -/** Error for ${a} */ -#define ERROR_BRACKETED_VARIABLE1 _( L"Variables cannot be bracketed. In fish, please use {$%ls}." ) +/// Error for "${a}". +#define ERROR_BRACKETED_VARIABLE_QUOTED1 \ + _(L"Variables cannot be bracketed. In fish, please use \"$%ls\".") -/** Error for "${a}" */ -#define ERROR_BRACKETED_VARIABLE_QUOTED1 _( L"Variables cannot be bracketed. In fish, please use \"$%ls\"." ) +/// Error issued on $?. +#define ERROR_NOT_STATUS _(L"$? is not the exit status. In fish, please use $status.") -/** Error issued on $? */ -#define ERROR_NOT_STATUS _( L"$? is not the exit status. In fish, please use $status.") +/// Error issued on $$. +#define ERROR_NOT_PID _(L"$$ is not the pid. In fish, please use %%self.") -/** Error issued on $$ */ -#define ERROR_NOT_PID _( L"$$ is not the pid. In fish, please use %%self.") +/// Error issued on $#. +#define ERROR_NOT_ARGV_COUNT _(L"$# is not supported. In fish, please use 'count $argv'.") -/** Error issued on $# */ -#define ERROR_NOT_ARGV_COUNT _( L"$# is not supported. In fish, please use 'count $argv'.") +/// Error issued on $@. +#define ERROR_NOT_ARGV_AT _(L"$@ is not supported. In fish, please use $argv.") -/** Error issued on $@ */ -#define ERROR_NOT_ARGV_AT _( L"$@ is not supported. In fish, please use $argv.") +/// Error issued on $(...). +#define ERROR_BAD_VAR_SUBCOMMAND1 _(L"$(...) is not supported. In fish, please use '(%ls)'.") -/** Error issued on $(...) */ -#define ERROR_BAD_VAR_SUBCOMMAND1 _( L"$(...) is not supported. In fish, please use '(%ls)'." ) +/// Error issued on $*. +#define ERROR_NOT_ARGV_STAR _(L"$* is not supported. In fish, please use $argv.") -/** Error issued on $* */ -#define ERROR_NOT_ARGV_STAR _( L"$* is not supported. In fish, please use $argv." ) +/// Error issued on $. +#define ERROR_NO_VAR_NAME _(L"Expected a variable name after this $.") -/** Error issued on $ */ -#define ERROR_NO_VAR_NAME _( L"Expected a variable name after this $.") +/// Error on ||. +#define ERROR_BAD_OR _(L"Unsupported use of '||'. In fish, please use 'COMMAND; or COMMAND'.") -/** Error on || */ -#define ERROR_BAD_OR _( L"Unsupported use of '||'. In fish, please use 'COMMAND; or COMMAND'.") +/// Error on &&. +#define ERROR_BAD_AND _(L"Unsupported use of '&&'. In fish, please use 'COMMAND; and COMMAND'.") -/** Error on && */ -#define ERROR_BAD_AND _( L"Unsupported use of '&&'. In fish, please use 'COMMAND; and COMMAND'.") +/// Error on foo=bar. +#define ERROR_BAD_EQUALS_IN_COMMAND5 \ + _(L"Unsupported use of '='. To run '%ls' with a modified environment, please use 'env " \ + L"%ls=%ls %ls%ls'") -/** Error on foo=bar */ -#define ERROR_BAD_EQUALS_IN_COMMAND5 _( L"Unsupported use of '='. To run '%ls' with a modified environment, please use 'env %ls=%ls %ls%ls'") +/// Error message for Posix-style assignment: foo=bar. +#define ERROR_BAD_COMMAND_ASSIGN_ERR_MSG \ + _(L"Unsupported use of '='. In fish, please use 'set %ls %ls'.") -/** Error message for Posix-style assignment: foo=bar */ -#define ERROR_BAD_COMMAND_ASSIGN_ERR_MSG _( L"Unsupported use of '='. In fish, please use 'set %ls %ls'.") +/// While block description. +#define WHILE_BLOCK N_(L"'while' block") +/// For block description. +#define FOR_BLOCK N_(L"'for' block") +/// Breakpoint block. +#define BREAKPOINT_BLOCK N_(L"Block created by breakpoint") -/** - While block description -*/ -#define WHILE_BLOCK N_( L"'while' block" ) +/// If block description. +#define IF_BLOCK N_(L"'if' conditional block") -/** - For block description -*/ -#define FOR_BLOCK N_( L"'for' block" ) +/// Function definition block description. +#define FUNCTION_DEF_BLOCK N_(L"function definition block") -/** - Breakpoint block -*/ -#define BREAKPOINT_BLOCK N_( L"Block created by breakpoint" ) +/// Function invocation block description. +#define FUNCTION_CALL_BLOCK N_(L"function invocation block") +/// Function invocation block description. +#define FUNCTION_CALL_NO_SHADOW_BLOCK N_(L"function invocation block with no variable shadowing") +/// Switch block description. +#define SWITCH_BLOCK N_(L"'switch' block") -/** - If block description -*/ -#define IF_BLOCK N_( L"'if' conditional block" ) +/// Fake block description. +#define FAKE_BLOCK N_(L"unexecutable block") +/// Top block description. +#define TOP_BLOCK N_(L"global root block") -/** - Function definition block description -*/ -#define FUNCTION_DEF_BLOCK N_( L"function definition block" ) +/// Command substitution block description. +#define SUBST_BLOCK N_(L"command substitution block") +/// Begin block description. +#define BEGIN_BLOCK N_(L"'begin' unconditional block") -/** - Function invocation block description -*/ -#define FUNCTION_CALL_BLOCK N_( L"function invocation block" ) - -/** - Function invocation block description -*/ -#define FUNCTION_CALL_NO_SHADOW_BLOCK N_( L"function invocation block with no variable shadowing" ) - - -/** - Switch block description -*/ -#define SWITCH_BLOCK N_( L"'switch' block" ) - - -/** - Fake block description -*/ -#define FAKE_BLOCK N_( L"unexecutable block" ) - - -/** - Top block description -*/ -#define TOP_BLOCK N_( L"global root block" ) - - -/** - Command substitution block description -*/ -#define SUBST_BLOCK N_( L"command substitution block" ) - - -/** - Begin block description -*/ -#define BEGIN_BLOCK N_( L"'begin' unconditional block" ) - - -/** - Source block description -*/ -#define SOURCE_BLOCK N_( L"Block created by the . builtin" ) - -/** - Source block description -*/ -#define EVENT_BLOCK N_( L"event handler block" ) - - -/** - Unknown block description -*/ -#define UNKNOWN_BLOCK N_( L"unknown/invalid block" ) +/// Source block description. +#define SOURCE_BLOCK N_(L"Block created by the . builtin") +/// Source block description. +#define EVENT_BLOCK N_(L"event handler block") +/// Unknown block description. +#define UNKNOWN_BLOCK N_(L"unknown/invalid block") #endif diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp index 202194e79..a553f4cbf 100644 --- a/src/parse_execution.cpp +++ b/src/parse_execution.cpp @@ -1,67 +1,67 @@ -/**\file parse_execution.cpp - - Provides the "linkage" between a parse_node_tree_t and actual execution structures (job_t, etc.) - - A note on error handling: fish has two kind of errors, fatal parse errors non-fatal runtime errors. A fatal error prevents execution of the entire file, while a non-fatal error skips that job. - - Non-fatal errors are printed as soon as they are encountered; otherwise you would have to wait for the execution to finish to see them. -*/ +// Provides the "linkage" between a parse_node_tree_t and actual execution structures (job_t, etc.) +// +// A note on error handling: fish has two kind of errors, fatal parse errors non-fatal runtime +// errors. A fatal error prevents execution of the entire file, while a non-fatal error skips that +// job. +// +// Non-fatal errors are printed as soon as they are encountered; otherwise you would have to wait +// for the execution to finish to see them. #include #include #include +#include #include #include #include #include #include #include -#include #include +#include #include -#include -#include "parse_execution.h" +#include "builtin.h" +#include "common.h" +#include "complete.h" #include "env.h" #include "event.h" -#include "tokenizer.h" -#include "util.h" -#include "parse_util.h" -#include "complete.h" -#include "wildcard.h" -#include "parser.h" -#include "expand.h" -#include "reader.h" -#include "wutil.h" -#include "path.h" -#include "function.h" -#include "builtin.h" #include "exec.h" -#include "common.h" +#include "expand.h" +#include "function.h" #include "io.h" #include "parse_constants.h" +#include "parse_execution.h" #include "parse_tree.h" +#include "parse_util.h" +#include "parser.h" +#include "path.h" #include "proc.h" +#include "reader.h" +#include "tokenizer.h" +#include "util.h" +#include "wildcard.h" +#include "wutil.h" -/* These are the specific statement types that support redirections */ -static bool specific_statement_type_is_redirectable_block(const parse_node_t &node) -{ - return node.type == symbol_block_statement || node.type == symbol_if_statement || node.type == symbol_switch_statement; - +/// These are the specific statement types that support redirections. +static bool specific_statement_type_is_redirectable_block(const parse_node_t &node) { + return node.type == symbol_block_statement || node.type == symbol_if_statement || + node.type == symbol_switch_statement; } -/* Get the name of a redirectable block, for profiling purposes */ -static wcstring profiling_cmd_name_for_redirectable_block(const parse_node_t &node, const parse_node_tree_t &tree, const wcstring &src) -{ +/// Get the name of a redirectable block, for profiling purposes. +static wcstring profiling_cmd_name_for_redirectable_block(const parse_node_t &node, + const parse_node_tree_t &tree, + const wcstring &src) { assert(specific_statement_type_is_redirectable_block(node)); assert(node.has_source()); - /* Get the source for the block, and cut it at the next statement terminator. */ + // Get the source for the block, and cut it at the next statement terminator. const size_t src_start = node.source_start; size_t src_len = node.source_length; - const parse_node_tree_t::parse_node_list_t statement_terminator_nodes = tree.find_nodes(node, parse_token_type_end, 1); - if (! statement_terminator_nodes.empty()) - { + const parse_node_tree_t::parse_node_list_t statement_terminator_nodes = + tree.find_nodes(node, parse_token_type_end, 1); + if (!statement_terminator_nodes.empty()) { const parse_node_t *term = statement_terminator_nodes.at(0); assert(term->source_start >= src_start); src_len = term->source_start - src_start; @@ -72,25 +72,31 @@ static wcstring profiling_cmd_name_for_redirectable_block(const parse_node_t &no return result; } -parse_execution_context_t::parse_execution_context_t(moved_ref t, const wcstring &s, parser_t *p, int initial_eval_level) : tree(t), src(s), parser(p), eval_level(initial_eval_level), executing_node_idx(NODE_OFFSET_INVALID), cached_lineno_offset(0), cached_lineno_count(0) -{ -} +parse_execution_context_t::parse_execution_context_t(moved_ref t, + const wcstring &s, parser_t *p, + int initial_eval_level) + : tree(t), + src(s), + parser(p), + eval_level(initial_eval_level), + executing_node_idx(NODE_OFFSET_INVALID), + cached_lineno_offset(0), + cached_lineno_count(0) {} -/* Utilities */ +// Utilities -wcstring parse_execution_context_t::get_source(const parse_node_t &node) const -{ +wcstring parse_execution_context_t::get_source(const parse_node_t &node) const { return node.get_source(this->src); } -const parse_node_t *parse_execution_context_t::get_child(const parse_node_t &parent, node_offset_t which, parse_token_type_t expected_type) const -{ +const parse_node_t *parse_execution_context_t::get_child(const parse_node_t &parent, + node_offset_t which, + parse_token_type_t expected_type) const { return this->tree.get_child(parent, which, expected_type); } -node_offset_t parse_execution_context_t::get_offset(const parse_node_t &node) const -{ - /* Get the offset of a node via pointer arithmetic, very hackish */ +node_offset_t parse_execution_context_t::get_offset(const parse_node_t &node) const { + // Get the offset of a node via pointer arithmetic, very hackish. const parse_node_t *addr = &node; const parse_node_t *base = &this->tree.at(0); assert(addr >= base); @@ -101,433 +107,388 @@ node_offset_t parse_execution_context_t::get_offset(const parse_node_t &node) co return offset; } -const parse_node_t *parse_execution_context_t::infinite_recursive_statement_in_job_list(const parse_node_t &job_list, wcstring *out_func_name) const -{ +const parse_node_t *parse_execution_context_t::infinite_recursive_statement_in_job_list( + const parse_node_t &job_list, wcstring *out_func_name) const { assert(job_list.type == symbol_job_list); - /* - 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. - */ + // 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. const block_t *current = parser->block_at_index(0), *parent = parser->block_at_index(1); - bool is_within_function_call = (current && parent && current->type() == TOP && parent->type() == FUNCTION_CALL); - if (! is_within_function_call) - { + bool is_within_function_call = + (current && parent && current->type() == TOP && parent->type() == FUNCTION_CALL); + if (!is_within_function_call) { return NULL; } - /* Check to see which function call is forbidden */ - if (parser->forbidden_function.empty()) - { + // Check to see which function call is forbidden. + if (parser->forbidden_function.empty()) { return NULL; } const wcstring &forbidden_function_name = parser->forbidden_function.back(); - /* Get the first job in the job list. */ + // Get the first job in the job list. const parse_node_t *first_job = tree.next_node_in_node_list(job_list, symbol_job, NULL); - if (first_job == NULL) - { + if (first_job == NULL) { return NULL; } - /* Here's the statement node we find that's infinite recursive */ + // Here's the statement node we find that's infinite recursive. const parse_node_t *infinite_recursive_statement = NULL; - /* Get the list of statements */ - const parse_node_tree_t::parse_node_list_t statements = tree.specific_statements_for_job(*first_job); + // Get the list of statements. + const parse_node_tree_t::parse_node_list_t statements = + tree.specific_statements_for_job(*first_job); - /* Find all the decorated statements. We are interested in statements with no decoration (i.e. not command, not builtin) whose command expands to the forbidden function */ - for (size_t i=0; i < statements.size(); i++) - { - /* We only care about decorated statements, not while statements, etc. */ + // Find all the decorated statements. We are interested in statements with no decoration (i.e. + // not command, not builtin) whose command expands to the forbidden function. + for (size_t i = 0; i < statements.size(); i++) { + // We only care about decorated statements, not while statements, etc. const parse_node_t &statement = *statements.at(i); - if (statement.type != symbol_decorated_statement) - { + if (statement.type != symbol_decorated_statement) { continue; } const parse_node_t &plain_statement = tree.find_child(statement, symbol_plain_statement); - if (tree.decoration_for_plain_statement(plain_statement) != parse_statement_decoration_none) - { - /* This statement has a decoration like 'builtin' or 'command', and therefore is not infinite recursion. In particular this is what enables 'wrapper functions' */ + if (tree.decoration_for_plain_statement(plain_statement) != + parse_statement_decoration_none) { + // This statement has a decoration like 'builtin' or 'command', and therefore is not + // infinite recursion. In particular this is what enables 'wrapper functions'. continue; } - /* Ok, this is an undecorated plain statement. Get and expand its command */ + // Ok, this is an undecorated plain statement. Get and expand its command. wcstring cmd; tree.command_for_plain_statement(plain_statement, src, &cmd); if (expand_one(cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES, NULL) && - cmd == forbidden_function_name) - { - /* This is it */ + cmd == forbidden_function_name) { + // This is it. infinite_recursive_statement = &statement; - if (out_func_name != NULL) - { + if (out_func_name != NULL) { *out_func_name = forbidden_function_name; } break; } } - assert(infinite_recursive_statement == NULL || infinite_recursive_statement->type == symbol_decorated_statement); + assert(infinite_recursive_statement == NULL || + infinite_recursive_statement->type == symbol_decorated_statement); return infinite_recursive_statement; } -enum process_type_t parse_execution_context_t::process_type_for_command(const parse_node_t &plain_statement, const wcstring &cmd) const -{ +enum process_type_t parse_execution_context_t::process_type_for_command( + const parse_node_t &plain_statement, const wcstring &cmd) const { assert(plain_statement.type == symbol_plain_statement); enum process_type_t process_type = EXTERNAL; - /* Determine the process type, which depends on the statement decoration (command, builtin, etc) */ - enum parse_statement_decoration_t decoration = tree.decoration_for_plain_statement(plain_statement); + // Determine the process type, which depends on the statement decoration (command, builtin, + // etc). + enum parse_statement_decoration_t decoration = + tree.decoration_for_plain_statement(plain_statement); - if (decoration == parse_statement_decoration_exec) - { - /* Always exec */ + if (decoration == parse_statement_decoration_exec) { + // Always exec. process_type = INTERNAL_EXEC; - } - else if (decoration == parse_statement_decoration_command) - { - /* Always a command */ + } else if (decoration == parse_statement_decoration_command) { + // Always a command. process_type = EXTERNAL; - } - else if (decoration == parse_statement_decoration_builtin) - { - /* What happens if this builtin is not valid? */ + } else if (decoration == parse_statement_decoration_builtin) { + // What happens if this builtin is not valid? process_type = INTERNAL_BUILTIN; - } - else if (function_exists(cmd)) - { + } else if (function_exists(cmd)) { process_type = INTERNAL_FUNCTION; - } - else if (builtin_exists(cmd)) - { + } else if (builtin_exists(cmd)) { process_type = INTERNAL_BUILTIN; - } - else - { + } else { process_type = EXTERNAL; } return process_type; } -bool parse_execution_context_t::should_cancel_execution(const block_t *block) const -{ +bool parse_execution_context_t::should_cancel_execution(const block_t *block) const { return cancellation_reason(block) != execution_cancellation_none; } -parse_execution_context_t::execution_cancellation_reason_t parse_execution_context_t::cancellation_reason(const block_t *block) const -{ - if (shell_is_exiting()) - { +parse_execution_context_t::execution_cancellation_reason_t +parse_execution_context_t::cancellation_reason(const block_t *block) const { + if (shell_is_exiting()) { return execution_cancellation_exit; - } - else if (parser && parser->cancellation_requested) - { + } else if (parser && parser->cancellation_requested) { return execution_cancellation_skip; - } - else if (block && block->loop_status != LOOP_NORMAL) - { - /* Nasty hack - break and continue set the 'skip' flag as well as the loop status flag. */ + } else if (block && block->loop_status != LOOP_NORMAL) { + // Nasty hack - break and continue set the 'skip' flag as well as the loop status flag. return execution_cancellation_loop_control; - } - else if (block && block->skip) - { + } else if (block && block->skip) { return execution_cancellation_skip; - } - else - { + } else { return execution_cancellation_none; } } -/* Return whether the job contains a single statement, of block type, with no redirections */ -bool parse_execution_context_t::job_is_simple_block(const parse_node_t &job_node) const -{ +/// Return whether the job contains a single statement, of block type, with no redirections. +bool parse_execution_context_t::job_is_simple_block(const parse_node_t &job_node) const { assert(job_node.type == symbol_job); - /* Must have one statement */ + // Must have one statement. const parse_node_t &statement = *get_child(job_node, 0, symbol_statement); const parse_node_t &specific_statement = *get_child(statement, 0); - if (! specific_statement_type_is_redirectable_block(specific_statement)) - { - /* Not an appropriate block type */ + if (!specific_statement_type_is_redirectable_block(specific_statement)) { + // Not an appropriate block type. return false; } - - /* Must be no pipes */ + // Must be no pipes. const parse_node_t &continuation = *get_child(job_node, 1, symbol_job_continuation); - if (continuation.child_count > 0) - { - /* Multiple statements in this job, so there's pipes involved */ + if (continuation.child_count > 0) { + // Multiple statements in this job, so there's pipes involved. return false; } - /* Check for arguments and redirections. All of the above types have an arguments / redirections list. It must be empty. */ - const parse_node_t &args_and_redirections = tree.find_child(specific_statement, symbol_arguments_or_redirections_list); - if (args_and_redirections.child_count > 0) - { - /* Non-empty, we have an argument or redirection */ + // Check for arguments and redirections. All of the above types have an arguments / redirections + // list. It must be empty. + const parse_node_t &args_and_redirections = + tree.find_child(specific_statement, symbol_arguments_or_redirections_list); + if (args_and_redirections.child_count > 0) { + // Non-empty, we have an argument or redirection. return false; } - /* Ok, we are a simple block! */ + // Ok, we are a simple block! return true; } -parse_execution_result_t parse_execution_context_t::run_if_statement(const parse_node_t &statement) -{ +parse_execution_result_t parse_execution_context_t::run_if_statement( + const parse_node_t &statement) { assert(statement.type == symbol_if_statement); - /* Push an if block */ + // Push an if block. if_block_t *ib = new if_block_t(); ib->node_offset = this->get_offset(statement); parser->push_block(ib); parse_execution_result_t result = parse_execution_success; - /* We have a sequence of if clauses, with a final else, resulting in a single job list that we execute */ + // We have a sequence of if clauses, with a final else, resulting in a single job list that we + // execute. const parse_node_t *job_list_to_execute = NULL; const parse_node_t *if_clause = get_child(statement, 0, symbol_if_clause); const parse_node_t *else_clause = get_child(statement, 1, symbol_else_clause); - for (;;) - { - if (should_cancel_execution(ib)) - { + for (;;) { + if (should_cancel_execution(ib)) { result = parse_execution_cancelled; break; } - - /* An if condition has a job and a "tail" of andor jobs, e.g. "foo ; and bar; or baz" */ + + // An if condition has a job and a "tail" of andor jobs, e.g. "foo ; and bar; or baz". assert(if_clause != NULL && else_clause != NULL); const parse_node_t &condition_head = *get_child(*if_clause, 1, symbol_job); - const parse_node_t &condition_boolean_tail = *get_child(*if_clause, 3, symbol_andor_job_list); + const parse_node_t &condition_boolean_tail = + *get_child(*if_clause, 3, symbol_andor_job_list); - /* Check the condition and the tail. We treat parse_execution_errored here as failure, in accordance with historic behavior */ + // Check the condition and the tail. We treat parse_execution_errored here as failure, in + // accordance with historic behavior. parse_execution_result_t cond_ret = run_1_job(condition_head, ib); - if (cond_ret == parse_execution_success) - { + if (cond_ret == parse_execution_success) { cond_ret = run_job_list(condition_boolean_tail, ib); } - const bool take_branch = (cond_ret == parse_execution_success) && proc_get_last_status() == EXIT_SUCCESS; + const bool take_branch = + (cond_ret == parse_execution_success) && proc_get_last_status() == EXIT_SUCCESS; - if (take_branch) - { - /* condition succeeded */ + if (take_branch) { + // Condition succeeded. job_list_to_execute = get_child(*if_clause, 4, symbol_job_list); break; - } - else if (else_clause->child_count == 0) - { - /* 'if' condition failed, no else clause, return 0, we're done. */ + } else if (else_clause->child_count == 0) { + // 'if' condition failed, no else clause, return 0, we're done. job_list_to_execute = NULL; proc_set_last_status(STATUS_BUILTIN_OK); break; - } - else - { - /* We have an 'else continuation' (either else-if or else) */ + } else { + // We have an 'else continuation' (either else-if or else). const parse_node_t &else_cont = *get_child(*else_clause, 1, symbol_else_continuation); const parse_node_t *maybe_if_clause = get_child(else_cont, 0); - if (maybe_if_clause && maybe_if_clause->type == symbol_if_clause) - { - /* it's an 'else if', go to the next one */ + if (maybe_if_clause && maybe_if_clause->type == symbol_if_clause) { + // it's an 'else if', go to the next one. if_clause = maybe_if_clause; else_clause = get_child(else_cont, 1, symbol_else_clause); - } - else - { - /* it's the final 'else', we're done */ + } else { + // It's the final 'else', we're done. job_list_to_execute = get_child(else_cont, 1, symbol_job_list); break; } } } - /* Execute any job list we got */ - if (job_list_to_execute != NULL) - { + // Execute any job list we got. + if (job_list_to_execute != NULL) { run_job_list(*job_list_to_execute, ib); - } - else - { /* No job list means no sucessful conditions, so return 0 (#1443). */ + } else { + // No job list means no sucessful conditions, so return 0 (issue #1443). proc_set_last_status(STATUS_BUILTIN_OK); } - /* It's possible there's a last-minute cancellation (#1297). */ - if (should_cancel_execution(ib)) - { + // It's possible there's a last-minute cancellation (issue #1297). + if (should_cancel_execution(ib)) { result = parse_execution_cancelled; } - /* Done */ + // Done parser->pop_block(ib); - /* Otherwise, take the exit status of the job list. Reversal of #1061. */ + // Otherwise, take the exit status of the job list. Reversal of issue #1061. return result; } -parse_execution_result_t parse_execution_context_t::run_begin_statement(const parse_node_t &header, const parse_node_t &contents) -{ +parse_execution_result_t parse_execution_context_t::run_begin_statement( + const parse_node_t &header, const parse_node_t &contents) { assert(header.type == symbol_begin_header); assert(contents.type == symbol_job_list); - /* Basic begin/end block. Push a scope block. */ + // Basic begin/end block. Push a scope block. scope_block_t *sb = new scope_block_t(BEGIN); parser->push_block(sb); - /* Run the job list */ + // Run the job list. parse_execution_result_t ret = run_job_list(contents, sb); - /* Pop the block */ + // Pop the block. parser->pop_block(sb); return ret; } -/* Define a function */ -parse_execution_result_t parse_execution_context_t::run_function_statement(const parse_node_t &header, const parse_node_t &block_end_command) -{ +// Define a function. +parse_execution_result_t parse_execution_context_t::run_function_statement( + const parse_node_t &header, const parse_node_t &block_end_command) { assert(header.type == symbol_function_header); assert(block_end_command.type == symbol_end_command); - - /* Get arguments */ + + // Get arguments. wcstring_list_t argument_list; parse_execution_result_t result = this->determine_arguments(header, &argument_list, failglob); - if (result == parse_execution_success) - { - /* The function definition extends from the end of the header to the function end. It's not just the range of the contents because that loses comments - see issue #1710 */ + if (result == parse_execution_success) { + // The function definition extends from the end of the header to the function end. It's not + // just the range of the contents because that loses comments - see issue #1710. assert(block_end_command.has_source()); size_t contents_start = header.source_start + header.source_length; - size_t contents_end = block_end_command.source_start; // 1 past the last character in the function definition + size_t contents_end = + block_end_command.source_start; // 1 past the last character in the function definition assert(contents_end >= contents_start); - - // Swallow whitespace at both ends - while (contents_start < contents_end && iswspace(this->src.at(contents_start))) - { + + // Swallow whitespace at both ends. + while (contents_start < contents_end && iswspace(this->src.at(contents_start))) { contents_start++; } - while (contents_start < contents_end && iswspace(this->src.at(contents_end - 1))) - { + while (contents_start < contents_end && iswspace(this->src.at(contents_end - 1))) { contents_end--; } - + assert(contents_end >= contents_start); - const wcstring contents_str = wcstring(this->src, contents_start, contents_end - contents_start); + const wcstring contents_str = + wcstring(this->src, contents_start, contents_end - contents_start); int definition_line_offset = this->line_offset_of_character_at_offset(contents_start); wcstring error_str; io_streams_t streams; - int err = define_function(*parser, streams, argument_list, contents_str, definition_line_offset, &error_str); + int err = define_function(*parser, streams, argument_list, contents_str, + definition_line_offset, &error_str); proc_set_last_status(err); - if (! error_str.empty()) - { + if (!error_str.empty()) { this->report_error(header, L"%ls", error_str.c_str()); result = parse_execution_errored; } } return result; - } -parse_execution_result_t parse_execution_context_t::run_block_statement(const parse_node_t &statement) -{ +parse_execution_result_t parse_execution_context_t::run_block_statement( + const parse_node_t &statement) { assert(statement.type == symbol_block_statement); - const parse_node_t &block_header = *get_child(statement, 0, symbol_block_header); //block header - const parse_node_t &header = *get_child(block_header, 0); //specific header type (e.g. for loop) - const parse_node_t &contents = *get_child(statement, 1, symbol_job_list); //block contents + const parse_node_t &block_header = + *get_child(statement, 0, symbol_block_header); // block header + const parse_node_t &header = + *get_child(block_header, 0); // specific header type (e.g. for loop) + const parse_node_t &contents = *get_child(statement, 1, symbol_job_list); // block contents parse_execution_result_t ret = parse_execution_success; - switch (header.type) - { - case symbol_for_header: + switch (header.type) { + case symbol_for_header: { ret = run_for_statement(header, contents); break; - - case symbol_while_header: + } + case symbol_while_header: { ret = run_while_statement(header, contents); break; - - case symbol_function_header: - { - const parse_node_t &function_end = *get_child(statement, 2, symbol_end_command); //the 'end' associated with the block + } + case symbol_function_header: { + const parse_node_t &function_end = *get_child( + statement, 2, symbol_end_command); // the 'end' associated with the block ret = run_function_statement(header, function_end); break; } - - case symbol_begin_header: + case symbol_begin_header: { ret = run_begin_statement(header, contents); break; - - default: + } + default: { fprintf(stderr, "Unexpected block header: %ls\n", header.describe().c_str()); PARSER_DIE(); break; + } } return ret; } -parse_execution_result_t parse_execution_context_t::run_for_statement(const parse_node_t &header, const parse_node_t &block_contents) -{ +parse_execution_result_t parse_execution_context_t::run_for_statement( + const parse_node_t &header, const parse_node_t &block_contents) { assert(header.type == symbol_for_header); assert(block_contents.type == symbol_job_list); - /* Get the variable name: `for var_name in ...`. We expand the variable name. It better result in just one. */ + // Get the variable name: `for var_name in ...`. We expand the variable name. It better result + // in just one. const parse_node_t &var_name_node = *get_child(header, 1, parse_token_type_string); wcstring for_var_name = get_source(var_name_node); - if (! expand_one(for_var_name, 0, NULL)) - { + if (!expand_one(for_var_name, 0, NULL)) { report_error(var_name_node, FAILED_EXPANSION_VARIABLE_NAME_ERR_MSG, for_var_name.c_str()); return parse_execution_errored; } - /* Get the contents to iterate over. */ + // Get the contents to iterate over. wcstring_list_t argument_sequence; parse_execution_result_t ret = this->determine_arguments(header, &argument_sequence, nullglob); - if (ret != parse_execution_success) - { + if (ret != parse_execution_success) { return ret; } for_block_t *fb = new for_block_t(); parser->push_block(fb); - /* Now drive the for loop. */ + // Now drive the for loop. const size_t arg_count = argument_sequence.size(); - for (size_t i=0; i < arg_count; i++) - { - if (should_cancel_execution(fb)) - { + for (size_t i = 0; i < arg_count; i++) { + if (should_cancel_execution(fb)) { ret = parse_execution_cancelled; break; } const wcstring &val = argument_sequence.at(i); - env_set(for_var_name, val.c_str(), ENV_LOCAL); + env_set(for_var_name, val.c_str(), ENV_LOCAL); fb->loop_status = LOOP_NORMAL; fb->skip = 0; this->run_job_list(block_contents, fb); - if (this->cancellation_reason(fb) == execution_cancellation_loop_control) - { - /* Handle break or continue */ - if (fb->loop_status == LOOP_CONTINUE) - { - /* Reset the loop state */ + if (this->cancellation_reason(fb) == execution_cancellation_loop_control) { + // Handle break or continue. + if (fb->loop_status == LOOP_CONTINUE) { + // Reset the loop state. fb->loop_status = LOOP_NORMAL; fb->skip = false; continue; - } - else if (fb->loop_status == LOOP_BREAK) - { + } else if (fb->loop_status == LOOP_BREAK) { break; } } @@ -538,99 +499,88 @@ parse_execution_result_t parse_execution_context_t::run_for_statement(const pars return ret; } - -parse_execution_result_t parse_execution_context_t::run_switch_statement(const parse_node_t &statement) -{ +parse_execution_result_t parse_execution_context_t::run_switch_statement( + const parse_node_t &statement) { assert(statement.type == symbol_switch_statement); parse_execution_result_t result = parse_execution_success; - /* Get the switch variable */ + // Get the switch variable. const parse_node_t &switch_value_node = *get_child(statement, 1, symbol_argument); const wcstring switch_value = get_source(switch_value_node); - /* Expand it. We need to offset any errors by the position of the string */ + // Expand it. We need to offset any errors by the position of the string. std::vector switch_values_expanded; parse_error_list_t errors; - int expand_ret = expand_string(switch_value, &switch_values_expanded, EXPAND_NO_DESCRIPTIONS, &errors); + int expand_ret = + expand_string(switch_value, &switch_values_expanded, EXPAND_NO_DESCRIPTIONS, &errors); parse_error_offset_source_start(&errors, switch_value_node.source_start); - switch (expand_ret) - { - case EXPAND_ERROR: - { + switch (expand_ret) { + case EXPAND_ERROR: { result = report_errors(errors); break; } - - case EXPAND_WILDCARD_NO_MATCH: - { + case EXPAND_WILDCARD_NO_MATCH: { result = report_unmatched_wildcard_error(switch_value_node); break; } - case EXPAND_WILDCARD_MATCH: - case EXPAND_OK: - { + case EXPAND_OK: { break; } } - if (result == parse_execution_success && switch_values_expanded.size() != 1) - { - result = report_error(switch_value_node, - _(L"switch: Expected exactly one argument, got %lu\n"), - switch_values_expanded.size()); + if (result == parse_execution_success && switch_values_expanded.size() != 1) { + result = + report_error(switch_value_node, _(L"switch: Expected exactly one argument, got %lu\n"), + switch_values_expanded.size()); } - if (result == parse_execution_success) - { + if (result == parse_execution_success) { const wcstring &switch_value_expanded = switch_values_expanded.at(0).completion; switch_block_t *sb = new switch_block_t(); parser->push_block(sb); - - /* Expand case statements */ + // Expand case statements. const parse_node_t *case_item_list = get_child(statement, 3, symbol_case_item_list); - /* Loop while we don't have a match but do have more of the list */ + // Loop while we don't have a match but do have more of the list. const parse_node_t *matching_case_item = NULL; - while (matching_case_item == NULL && case_item_list != NULL) - { - if (should_cancel_execution(sb)) - { + while (matching_case_item == NULL && case_item_list != NULL) { + if (should_cancel_execution(sb)) { result = parse_execution_cancelled; break; } - /* Get the next item and the remainder of the list */ - const parse_node_t *case_item = tree.next_node_in_node_list(*case_item_list, symbol_case_item, &case_item_list); - if (case_item == NULL) - { - /* No more items */ + // Get the next item and the remainder of the list. + const parse_node_t *case_item = + tree.next_node_in_node_list(*case_item_list, symbol_case_item, &case_item_list); + if (case_item == NULL) { + // No more items. break; } - /* Pull out the argument list */ + // Pull out the argument list. const parse_node_t &arg_list = *get_child(*case_item, 1, symbol_argument_list); - /* Expand arguments. A case item list may have a wildcard that fails to expand to anything. We also report case errors, but don't stop execution; i.e. a case item that contains an unexpandable process will report and then fail to match. */ + // Expand arguments. A case item list may have a wildcard that fails to expand to + // anything. We also report case errors, but don't stop execution; i.e. a case item that + // contains an unexpandable process will report and then fail to match. wcstring_list_t case_args; - parse_execution_result_t case_result = this->determine_arguments(arg_list, &case_args, failglob); - if (case_result == parse_execution_success) - { - for (size_t i=0; i < case_args.size(); i++) - { + parse_execution_result_t case_result = + this->determine_arguments(arg_list, &case_args, failglob); + if (case_result == parse_execution_success) { + for (size_t i = 0; i < case_args.size(); i++) { const wcstring &arg = case_args.at(i); - /* Unescape wildcards so they can be expanded again */ + // Unescape wildcards so they can be expanded again. wcstring unescaped_arg = parse_util_unescape_wildcards(arg); bool match = wildcard_match(switch_value_expanded, unescaped_arg); - /* If this matched, we're done */ - if (match) - { + // If this matched, we're done. + if (match) { matching_case_item = case_item; break; } @@ -638,9 +588,8 @@ parse_execution_result_t parse_execution_context_t::run_switch_statement(const p } } - if (result == parse_execution_success && matching_case_item != NULL) - { - /* Success, evaluate the job list */ + if (result == parse_execution_success && matching_case_item != NULL) { + // Success, evaluate the job list. const parse_node_t *job_list = get_child(*matching_case_item, 3, symbol_job_list); result = this->run_job_list(*job_list, sb); } @@ -651,68 +600,59 @@ parse_execution_result_t parse_execution_context_t::run_switch_statement(const p return result; } -parse_execution_result_t parse_execution_context_t::run_while_statement(const parse_node_t &header, const parse_node_t &block_contents) -{ +parse_execution_result_t parse_execution_context_t::run_while_statement( + const parse_node_t &header, const parse_node_t &block_contents) { assert(header.type == symbol_while_header); assert(block_contents.type == symbol_job_list); - /* Push a while block */ + // Push a while block. while_block_t *wb = new while_block_t(); wb->node_offset = this->get_offset(header); parser->push_block(wb); parse_execution_result_t ret = parse_execution_success; - /* The conditions of the while loop */ + // The conditions of the while loop. const parse_node_t &condition_head = *get_child(header, 1, symbol_job); const parse_node_t &condition_boolean_tail = *get_child(header, 3, symbol_andor_job_list); - /* Run while the condition is true */ - for (;;) - { - /* Check the condition */ + // Run while the condition is true. + for (;;) { + // Check the condition. parse_execution_result_t cond_ret = this->run_1_job(condition_head, wb); - if (cond_ret == parse_execution_success) - { + if (cond_ret == parse_execution_success) { cond_ret = run_job_list(condition_boolean_tail, wb); } - - /* We only continue on successful execution and EXIT_SUCCESS */ - if (cond_ret != parse_execution_success || proc_get_last_status() != EXIT_SUCCESS) - { + + // We only continue on successful execution and EXIT_SUCCESS. + if (cond_ret != parse_execution_success || proc_get_last_status() != EXIT_SUCCESS) { break; } - /* Check cancellation */ - if (this->should_cancel_execution(wb)) - { + // Check cancellation. + if (this->should_cancel_execution(wb)) { ret = parse_execution_cancelled; break; } - - /* The block ought to go inside the loop (see #1212) */ + // The block ought to go inside the loop (see issue #1212). this->run_job_list(block_contents, wb); - if (this->cancellation_reason(wb) == execution_cancellation_loop_control) - { - /* Handle break or continue */ - if (wb->loop_status == LOOP_CONTINUE) - { - /* Reset the loop state */ + if (this->cancellation_reason(wb) == execution_cancellation_loop_control) { + // Handle break or continue. + if (wb->loop_status == LOOP_CONTINUE) { + // Reset the loop state. wb->loop_status = LOOP_NORMAL; wb->skip = false; continue; - } - else if (wb->loop_status == LOOP_BREAK) - { + } else if (wb->loop_status == LOOP_BREAK) { break; } } - - /* no_exec means that fish was invoked with -n or --no-execute. If set, we allow the loop to not-execute once so its contents can be checked, and then break */ - if (no_exec) - { + + // no_exec means that fish was invoked with -n or --no-execute. If set, we allow the loop to + // not-execute once so its contents can be checked, and then break. + if (no_exec) { break; } } @@ -723,15 +663,16 @@ parse_execution_result_t parse_execution_context_t::run_while_statement(const pa return ret; } -/* Reports an error. Always returns parse_execution_errored, so you can assign the result to an 'errored' variable */ -parse_execution_result_t parse_execution_context_t::report_error(const parse_node_t &node, const wchar_t *fmt, ...) const -{ - /* Create an error */ +// Reports an error. Always returns parse_execution_errored, so you can assign the result to an +// 'errored' variable. +parse_execution_result_t parse_execution_context_t::report_error(const parse_node_t &node, + const wchar_t *fmt, ...) const { + // Create an error. parse_error_list_t error_list = parse_error_list_t(1); parse_error_t *error = &error_list.at(0); error->source_start = node.source_start; error->source_length = node.source_length; - error->code = parse_error_syntax; //hackish + error->code = parse_error_syntax; // hackish va_list va; va_start(va, fmt); @@ -742,109 +683,85 @@ parse_execution_result_t parse_execution_context_t::report_error(const parse_nod return parse_execution_errored; } -parse_execution_result_t parse_execution_context_t::report_errors(const parse_error_list_t &error_list) const -{ - if (! parser->cancellation_requested) - { - if (error_list.empty()) - { +parse_execution_result_t parse_execution_context_t::report_errors( + const parse_error_list_t &error_list) const { + if (!parser->cancellation_requested) { + if (error_list.empty()) { fprintf(stderr, "Bug: Error reported but no error text found."); } - /* Get a backtrace */ + // Get a backtrace. wcstring backtrace_and_desc; parser->get_backtrace(src, error_list, &backtrace_and_desc); - /* Print it */ + // Print it. fprintf(stderr, "%ls", backtrace_and_desc.c_str()); } return parse_execution_errored; } -/* Reports an unmatched wildcard error and returns parse_execution_errored */ -parse_execution_result_t parse_execution_context_t::report_unmatched_wildcard_error(const parse_node_t &unmatched_wildcard) -{ +/// Reports an unmatched wildcard error and returns parse_execution_errored. +parse_execution_result_t parse_execution_context_t::report_unmatched_wildcard_error( + const parse_node_t &unmatched_wildcard) { proc_set_last_status(STATUS_UNMATCHED_WILDCARD); report_error(unmatched_wildcard, WILDCARD_ERR_MSG, get_source(unmatched_wildcard).c_str()); return parse_execution_errored; } -/* Handle the case of command not found */ -parse_execution_result_t parse_execution_context_t::handle_command_not_found(const wcstring &cmd_str, const parse_node_t &statement_node, int err_code) -{ +/// Handle the case of command not found. +parse_execution_result_t parse_execution_context_t::handle_command_not_found( + const wcstring &cmd_str, const parse_node_t &statement_node, int err_code) { assert(statement_node.type == symbol_plain_statement); - /* We couldn't find the specified command. This is a non-fatal error. We want to set the exit status to 127, which is the standard number used by other shells like bash and zsh. */ + // We couldn't find the specified command. This is a non-fatal error. We want to set the exit + // status to 127, which is the standard number used by other shells like bash and zsh. - const wchar_t * const cmd = cmd_str.c_str(); - const wchar_t * const equals_ptr = wcschr(cmd, L'='); - if (equals_ptr != NULL) - { - /* Try to figure out if this is a pure variable assignment (foo=bar), or if this appears to be running a command (foo=bar ruby...) */ + const wchar_t *const cmd = cmd_str.c_str(); + const wchar_t *const equals_ptr = wcschr(cmd, L'='); + if (equals_ptr != NULL) { + // Try to figure out if this is a pure variable assignment (foo=bar), or if this appears to + // be running a command (foo=bar ruby...). + const wcstring name_str = wcstring(cmd, equals_ptr - cmd); // variable name, up to the = + const wcstring val_str = wcstring(equals_ptr + 1); // variable value, past the = - const wcstring name_str = wcstring(cmd, equals_ptr - cmd); //variable name, up to the = - const wcstring val_str = wcstring(equals_ptr + 1); //variable value, past the = + const parse_node_tree_t::parse_node_list_t args = + tree.find_nodes(statement_node, symbol_argument, 1); - - const parse_node_tree_t::parse_node_list_t args = tree.find_nodes(statement_node, symbol_argument, 1); - - if (! args.empty()) - { + if (!args.empty()) { const wcstring argument = get_source(*args.at(0)); wcstring ellipsis_str = wcstring(1, ellipsis_char); - if (ellipsis_str == L"$") - ellipsis_str = L"..."; + if (ellipsis_str == L"$") ellipsis_str = L"..."; - /* Looks like a command */ - this->report_error(statement_node, - ERROR_BAD_EQUALS_IN_COMMAND5, - argument.c_str(), - name_str.c_str(), - val_str.c_str(), - argument.c_str(), + // Looks like a command. + this->report_error(statement_node, ERROR_BAD_EQUALS_IN_COMMAND5, argument.c_str(), + name_str.c_str(), val_str.c_str(), argument.c_str(), ellipsis_str.c_str()); - } - else - { - this->report_error(statement_node, - ERROR_BAD_COMMAND_ASSIGN_ERR_MSG, - name_str.c_str(), + } else { + this->report_error(statement_node, ERROR_BAD_COMMAND_ASSIGN_ERR_MSG, name_str.c_str(), val_str.c_str()); } - } - else if ((cmd[0] == L'$' || cmd[0] == VARIABLE_EXPAND || cmd[0] == VARIABLE_EXPAND_SINGLE) && cmd[1] != L'\0') - { - this->report_error(statement_node, - _(L"Variables may not be used as commands. In fish, please define a function or use 'eval %ls'."), + } else if ((cmd[0] == L'$' || cmd[0] == VARIABLE_EXPAND || cmd[0] == VARIABLE_EXPAND_SINGLE) && + cmd[1] != L'\0') { + this->report_error(statement_node, _(L"Variables may not be used as commands. In fish, " + L"please define a function or use 'eval %ls'."), cmd); - } - else if (wcschr(cmd, L'$')) - { - this->report_error(statement_node, - _(L"Commands may not contain variables. In fish, please use 'eval %ls'."), - cmd); - } - else if (err_code!=ENOENT) - { - this->report_error(statement_node, - _(L"The file '%ls' is not executable by this user"), - cmd?cmd:L"UNKNOWN"); - } - else - { - /* - Handle unrecognized commands with standard - command not found handler that can make better - error messages - */ - + } else if (wcschr(cmd, L'$')) { + this->report_error( + statement_node, + _(L"Commands may not contain variables. In fish, please use 'eval %ls'."), cmd); + } else if (err_code != ENOENT) { + this->report_error(statement_node, _(L"The file '%ls' is not executable by this user"), + cmd ? cmd : L"UNKNOWN"); + } else { + // Handle unrecognized commands with standard command not found handler that can make better + // error messages. wcstring_list_t event_args; { - parse_execution_result_t arg_result = this->determine_arguments(statement_node, &event_args, failglob); + parse_execution_result_t arg_result = + this->determine_arguments(statement_node, &event_args, failglob); - if (arg_result != parse_execution_success) - { + if (arg_result != parse_execution_success) { return arg_result; } @@ -853,114 +770,110 @@ parse_execution_result_t parse_execution_context_t::handle_command_not_found(con event_fire_generic(L"fish_command_not_found", &event_args); - /* Here we want to report an error (so it shows a backtrace), but with no text */ + // Here we want to report an error (so it shows a backtrace), but with no text. this->report_error(statement_node, L""); } - /* Set the last proc status appropriately */ - proc_set_last_status(err_code==ENOENT?STATUS_UNKNOWN_COMMAND:STATUS_NOT_EXECUTABLE); + // Set the last proc status appropriately. + proc_set_last_status(err_code == ENOENT ? STATUS_UNKNOWN_COMMAND : STATUS_NOT_EXECUTABLE); return parse_execution_errored; } -/* Creates a 'normal' (non-block) process */ -parse_execution_result_t parse_execution_context_t::populate_plain_process(job_t *job, process_t *proc, const parse_node_t &statement) -{ +/// Creates a 'normal' (non-block) process. +parse_execution_result_t parse_execution_context_t::populate_plain_process( + job_t *job, process_t *proc, const parse_node_t &statement) { assert(job != NULL); assert(proc != NULL); assert(statement.type == symbol_plain_statement); - /* We may decide that a command should be an implicit cd */ + // We may decide that a command should be an implicit cd. bool use_implicit_cd = false; - /* Get the command. We expect to always get it here. */ + // Get the command. We expect to always get it here. wcstring cmd; bool got_cmd = tree.command_for_plain_statement(statement, src, &cmd); assert(got_cmd); - /* Expand it as a command. Return an error on failure. */ + // Expand it as a command. Return an error on failure. bool expanded = expand_one(cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES, NULL); - if (! expanded) - { + if (!expanded) { report_error(statement, ILLEGAL_CMD_ERR_MSG, cmd.c_str()); return parse_execution_errored; } - /* Determine the process type */ + // Determine the process type. enum process_type_t process_type = process_type_for_command(statement, cmd); - /* Check for stack overflow */ - if (process_type == INTERNAL_FUNCTION && parser->forbidden_function.size() > FISH_MAX_STACK_DEPTH) - { + // Check for stack overflow. + if (process_type == INTERNAL_FUNCTION && + parser->forbidden_function.size() > FISH_MAX_STACK_DEPTH) { this->report_error(statement, CALL_STACK_LIMIT_EXCEEDED_ERR_MSG); return parse_execution_errored; } wcstring path_to_external_command; - if (process_type == EXTERNAL || process_type == INTERNAL_EXEC) - { - /* Determine the actual command. This may be an implicit cd. */ + if (process_type == EXTERNAL || process_type == INTERNAL_EXEC) { + // Determine the actual command. This may be an implicit cd. bool has_command = path_get_path(cmd, &path_to_external_command); - /* If there was no command, then we care about the value of errno after checking for it, to distinguish between e.g. no file vs permissions problem */ + // If there was no command, then we care about the value of errno after checking for it, to + // distinguish between e.g. no file vs permissions problem. const int no_cmd_err_code = errno; - /* If the specified command does not exist, and is undecorated, try using an implicit cd. */ - if (! has_command && tree.decoration_for_plain_statement(statement) == parse_statement_decoration_none) - { - /* Implicit cd requires an empty argument and redirection list */ - const parse_node_t *args = get_child(statement, 1, symbol_arguments_or_redirections_list); - if (args->child_count == 0) - { - /* Ok, no arguments or redirections; check to see if the first argument is a directory */ + // If the specified command does not exist, and is undecorated, try using an implicit cd. + if (!has_command && + tree.decoration_for_plain_statement(statement) == parse_statement_decoration_none) { + // Implicit cd requires an empty argument and redirection list. + const parse_node_t *args = + get_child(statement, 1, symbol_arguments_or_redirections_list); + if (args->child_count == 0) { + // Ok, no arguments or redirections; check to see if the first argument is a + // directory. wcstring implicit_cd_path; use_implicit_cd = path_can_be_implicit_cd(cmd, &implicit_cd_path); } } - if (! has_command && ! use_implicit_cd) - { - /* No command */ + if (!has_command && !use_implicit_cd) { + // No command. return this->handle_command_not_found(cmd, statement, no_cmd_err_code); } } - /* The argument list and set of IO redirections that we will construct for the process */ + // The argument list and set of IO redirections that we will construct for the process. io_chain_t process_io_chain; wcstring_list_t argument_list; - if (use_implicit_cd) - { + if (use_implicit_cd) { /* Implicit cd is simple */ argument_list.push_back(L"cd"); argument_list.push_back(cmd); path_to_external_command.clear(); - /* If we have defined a wrapper around cd, use it, otherwise use the cd builtin */ + // If we have defined a wrapper around cd, use it, otherwise use the cd builtin. process_type = function_exists(L"cd") ? INTERNAL_FUNCTION : INTERNAL_BUILTIN; - } - else - { + } else { const globspec_t glob_behavior = contains(cmd, L"set", L"count") ? nullglob : failglob; - /* Form the list of arguments. The command is the first argument. TODO: count hack, where we treat 'count --help' as different from 'count $foo' that expands to 'count --help'. fish 1.x never successfully did this, but it tried to! */ - parse_execution_result_t arg_result = this->determine_arguments(statement, &argument_list, glob_behavior); - if (arg_result != parse_execution_success) - { + // Form the list of arguments. The command is the first argument. TODO: count hack, where we + // treat 'count --help' as different from 'count $foo' that expands to 'count --help'. fish + // 1.x never successfully did this, but it tried to! + parse_execution_result_t arg_result = + this->determine_arguments(statement, &argument_list, glob_behavior); + if (arg_result != parse_execution_success) { return arg_result; } argument_list.insert(argument_list.begin(), cmd); - /* The set of IO redirections that we construct for the process */ - if (! this->determine_io_chain(statement, &process_io_chain)) - { + // The set of IO redirections that we construct for the process. + if (!this->determine_io_chain(statement, &process_io_chain)) { return parse_execution_errored; } - /* Determine the process type */ + // Determine the process type. process_type = process_type_for_command(statement, cmd); } - - /* Populate the process */ + // Populate the process. proc->type = process_type; proc->set_argv(argument_list); proc->set_io_chain(process_io_chain); @@ -968,58 +881,53 @@ parse_execution_result_t parse_execution_context_t::populate_plain_process(job_t return parse_execution_success; } -/* Determine the list of arguments, expanding stuff. Reports any errors caused by expansion. If we have a wildcard that could not be expanded, report the error and continue. */ -parse_execution_result_t parse_execution_context_t::determine_arguments(const parse_node_t &parent, wcstring_list_t *out_arguments, globspec_t glob_behavior) -{ - /* Get all argument nodes underneath the statement. We guess we'll have that many arguments (but may have more or fewer, if there are wildcards involved) */ - const parse_node_tree_t::parse_node_list_t argument_nodes = tree.find_nodes(parent, symbol_argument); +// Determine the list of arguments, expanding stuff. Reports any errors caused by expansion. If we +// have a wildcard that could not be expanded, report the error and continue. +parse_execution_result_t parse_execution_context_t::determine_arguments( + const parse_node_t &parent, wcstring_list_t *out_arguments, globspec_t glob_behavior) { + // Get all argument nodes underneath the statement. We guess we'll have that many arguments (but + // may have more or fewer, if there are wildcards involved). + const parse_node_tree_t::parse_node_list_t argument_nodes = + tree.find_nodes(parent, symbol_argument); out_arguments->reserve(out_arguments->size() + argument_nodes.size()); std::vector arg_expanded; - for (size_t i=0; i < argument_nodes.size(); i++) - { + for (size_t i = 0; i < argument_nodes.size(); i++) { const parse_node_t &arg_node = *argument_nodes.at(i); - /* Expect all arguments to have source */ + // Expect all arguments to have source. assert(arg_node.has_source()); const wcstring arg_str = arg_node.get_source(src); - /* Expand this string */ + // Expand this string. parse_error_list_t errors; arg_expanded.clear(); int expand_ret = expand_string(arg_str, &arg_expanded, EXPAND_NO_DESCRIPTIONS, &errors); parse_error_offset_source_start(&errors, arg_node.source_start); - switch (expand_ret) - { - case EXPAND_ERROR: - { + switch (expand_ret) { + case EXPAND_ERROR: { this->report_errors(errors); return parse_execution_errored; } - - case EXPAND_WILDCARD_NO_MATCH: - { - if (glob_behavior == failglob) - { - // report the unmatched wildcard error and stop processing + case EXPAND_WILDCARD_NO_MATCH: { + if (glob_behavior == failglob) { + // Report the unmatched wildcard error and stop processing. report_unmatched_wildcard_error(arg_node); return parse_execution_errored; } break; } - case EXPAND_WILDCARD_MATCH: - case EXPAND_OK: - { + case EXPAND_OK: { break; } } - /* Now copy over any expanded arguments. Do it using swap() to avoid extra allocations; this is called very frequently. */ + // Now copy over any expanded arguments. Do it using swap() to avoid extra allocations; this + // is called very frequently. size_t old_arg_count = out_arguments->size(); size_t new_arg_count = arg_expanded.size(); out_arguments->resize(old_arg_count + new_arg_count); - for (size_t i=0; i < new_arg_count; i++) - { + for (size_t i = 0; i < new_arg_count; i++) { wcstring &new_arg = arg_expanded.at(i).completion; out_arguments->at(old_arg_count + i).swap(new_arg); } @@ -1028,143 +936,130 @@ parse_execution_result_t parse_execution_context_t::determine_arguments(const pa return parse_execution_success; } -bool parse_execution_context_t::determine_io_chain(const parse_node_t &statement_node, io_chain_t *out_chain) -{ +bool parse_execution_context_t::determine_io_chain(const parse_node_t &statement_node, + io_chain_t *out_chain) { io_chain_t result; bool errored = false; - /* We are called with a statement of varying types. We require that the statement have an arguments_or_redirections_list child. */ - const parse_node_t &args_and_redirections_list = tree.find_child(statement_node, symbol_arguments_or_redirections_list); + // We are called with a statement of varying types. We require that the statement have an + // arguments_or_redirections_list child. + const parse_node_t &args_and_redirections_list = + tree.find_child(statement_node, symbol_arguments_or_redirections_list); - /* Get all redirection nodes underneath the statement */ - const parse_node_tree_t::parse_node_list_t redirect_nodes = tree.find_nodes(args_and_redirections_list, symbol_redirection); - for (size_t i=0; i < redirect_nodes.size(); i++) - { + // Get all redirection nodes underneath the statement. + const parse_node_tree_t::parse_node_list_t redirect_nodes = + tree.find_nodes(args_and_redirections_list, symbol_redirection); + for (size_t i = 0; i < redirect_nodes.size(); i++) { const parse_node_t &redirect_node = *redirect_nodes.at(i); - int source_fd = -1; /* source fd */ - wcstring target; /* file path or target fd */ - enum token_type redirect_type = tree.type_for_redirection(redirect_node, src, &source_fd, &target); + int source_fd = -1; // source fd + wcstring target; // file path or target fd + enum token_type redirect_type = + tree.type_for_redirection(redirect_node, src, &source_fd, &target); - /* PCA: I can't justify this EXPAND_SKIP_VARIABLES flag. It was like this when I got here. */ + // PCA: I can't justify this EXPAND_SKIP_VARIABLES flag. It was like this when I got here. bool target_expanded = expand_one(target, no_exec ? EXPAND_SKIP_VARIABLES : 0, NULL); - if (! target_expanded || target.empty()) - { - /* Should improve this error message */ - errored = report_error(redirect_node, - _(L"Invalid redirection target: %ls"), - target.c_str()); + if (!target_expanded || target.empty()) { + // TODO: Improve this error message. + errored = + report_error(redirect_node, _(L"Invalid redirection target: %ls"), target.c_str()); } - - /* Generate the actual IO redirection */ + // Generate the actual IO redirection. shared_ptr new_io; assert(redirect_type != TOK_NONE); - switch (redirect_type) - { - case TOK_REDIRECT_FD: - { - if (target == L"-") - { + switch (redirect_type) { + case TOK_REDIRECT_FD: { + if (target == L"-") { new_io.reset(new io_close_t(source_fd)); - } - else - { + } else { wchar_t *end = NULL; errno = 0; int old_fd = fish_wcstoi(target.c_str(), &end, 10); - if (old_fd < 0 || errno || *end) - { - errored = report_error(redirect_node, - _(L"Requested redirection to '%ls', which is not a valid file descriptor"), - target.c_str()); - } - else - { + if (old_fd < 0 || errno || *end) { + errored = + report_error(redirect_node, _(L"Requested redirection to '%ls', which " + L"is not a valid file descriptor"), + target.c_str()); + } else { new_io.reset(new io_fd_t(source_fd, old_fd, true)); } } break; } - case TOK_REDIRECT_OUT: case TOK_REDIRECT_APPEND: case TOK_REDIRECT_IN: - case TOK_REDIRECT_NOCLOB: - { + case TOK_REDIRECT_NOCLOB: { int oflags = oflags_for_redirection_type(redirect_type); io_file_t *new_io_file = new io_file_t(source_fd, target, oflags); new_io.reset(new_io_file); break; } - - default: - { - // Should be unreachable - fprintf(stderr, "Unexpected redirection type %ld. aborting.\n", (long)redirect_type); + default: { + // Should be unreachable. + fprintf(stderr, "Unexpected redirection type %ld. aborting.\n", + (long)redirect_type); PARSER_DIE(); break; } } - /* Append the new_io if we got one */ - if (new_io.get() != NULL) - { + // Append the new_io if we got one. + if (new_io.get() != NULL) { result.push_back(new_io); } } - if (out_chain && ! errored) - { + if (out_chain && !errored) { out_chain->swap(result); } - return ! errored; + return !errored; } -parse_execution_result_t parse_execution_context_t::populate_boolean_process(job_t *job, process_t *proc, const parse_node_t &bool_statement) -{ - // Handle a boolean statement +parse_execution_result_t parse_execution_context_t::populate_boolean_process( + job_t *job, process_t *proc, const parse_node_t &bool_statement) { + // Handle a boolean statement. bool skip_job = false; assert(bool_statement.type == symbol_boolean_statement); - switch (parse_node_tree_t::statement_boolean_type(bool_statement)) - { - case parse_bool_and: + switch (parse_node_tree_t::statement_boolean_type(bool_statement)) { + case parse_bool_and: { // AND. Skip if the last job failed. skip_job = (proc_get_last_status() != 0); break; - - case parse_bool_or: + } + case parse_bool_or: { // OR. Skip if the last job succeeded. skip_job = (proc_get_last_status() == 0); break; - - case parse_bool_not: + } + case parse_bool_not: { // NOT. Negate it. job_set_flag(job, JOB_NEGATE, !job_get_flag(job, JOB_NEGATE)); break; + } } - if (skip_job) - { + if (skip_job) { return parse_execution_skipped; - } - else - { + } else { const parse_node_t &subject = *tree.get_child(bool_statement, 1, symbol_statement); return this->populate_job_process(job, proc, subject); } } -parse_execution_result_t parse_execution_context_t::populate_block_process(job_t *job, process_t *proc, const parse_node_t &statement_node) -{ - /* We handle block statements by creating INTERNAL_BLOCK_NODE, that will bounce back to us when it's time to execute them */ - assert(statement_node.type == symbol_block_statement || statement_node.type == symbol_if_statement || statement_node.type == symbol_switch_statement); +parse_execution_result_t parse_execution_context_t::populate_block_process( + job_t *job, process_t *proc, const parse_node_t &statement_node) { + // We handle block statements by creating INTERNAL_BLOCK_NODE, that will bounce back to us when + // it's time to execute them. + assert(statement_node.type == symbol_block_statement || + statement_node.type == symbol_if_statement || + statement_node.type == symbol_switch_statement); - /* The set of IO redirections that we construct for the process */ + // The set of IO redirections that we construct for the process. io_chain_t process_io_chain; - bool errored = ! this->determine_io_chain(statement_node, &process_io_chain); - if (errored) - return parse_execution_errored; + bool errored = !this->determine_io_chain(statement_node, &process_io_chain); + if (errored) return parse_execution_errored; proc->type = INTERNAL_BLOCK_NODE; proc->internal_block_node = this->get_offset(statement_node); @@ -1172,116 +1067,105 @@ parse_execution_result_t parse_execution_context_t::populate_block_process(job_t return parse_execution_success; } - -/* Returns a process_t allocated with new. It's the caller's responsibility to delete it (!) */ -parse_execution_result_t parse_execution_context_t::populate_job_process(job_t *job, process_t *proc, const parse_node_t &statement_node) -{ +// Returns a process_t allocated with new. It's the caller's responsibility to delete it (!). +parse_execution_result_t parse_execution_context_t::populate_job_process( + job_t *job, process_t *proc, const parse_node_t &statement_node) { assert(statement_node.type == symbol_statement); assert(statement_node.child_count == 1); - // Get the "specific statement" which is boolean / block / if / switch / decorated + // Get the "specific statement" which is boolean / block / if / switch / decorated. const parse_node_t &specific_statement = *get_child(statement_node, 0); parse_execution_result_t result = parse_execution_success; - switch (specific_statement.type) - { - case symbol_boolean_statement: - { + switch (specific_statement.type) { + case symbol_boolean_statement: { result = this->populate_boolean_process(job, proc, specific_statement); break; } - case symbol_block_statement: case symbol_if_statement: - case symbol_switch_statement: - { + case symbol_switch_statement: { result = this->populate_block_process(job, proc, specific_statement); break; } - - case symbol_decorated_statement: - { - /* Get the plain statement. It will pull out the decoration itself */ - const parse_node_t &plain_statement = tree.find_child(specific_statement, symbol_plain_statement); + case symbol_decorated_statement: { + // Get the plain statement. It will pull out the decoration itself. + const parse_node_t &plain_statement = + tree.find_child(specific_statement, symbol_plain_statement); result = this->populate_plain_process(job, proc, plain_statement); break; } - - default: - fprintf(stderr, "'%ls' not handled by new parser yet\n", specific_statement.describe().c_str()); + default: { + fprintf(stderr, "'%ls' not handled by new parser yet\n", + specific_statement.describe().c_str()); PARSER_DIE(); break; + } } return result; } - -parse_execution_result_t parse_execution_context_t::populate_job_from_job_node(job_t *j, const parse_node_t &job_node, const block_t *associated_block) -{ +parse_execution_result_t parse_execution_context_t::populate_job_from_job_node( + job_t *j, const parse_node_t &job_node, const block_t *associated_block) { assert(job_node.type == symbol_job); - /* Tell the job what its command is */ + // Tell the job what its command is. j->set_command(get_source(job_node)); - /* We are going to construct process_t structures for every statement in the job. Get the first statement. */ + // We are going to construct process_t structures for every statement in the job. Get the first + // statement. const parse_node_t *statement_node = get_child(job_node, 0, symbol_statement); assert(statement_node != NULL); parse_execution_result_t result = parse_execution_success; - /* Create processes. Each one may fail. */ + // Create processes. Each one may fail. std::vector processes; processes.push_back(new process_t()); result = this->populate_job_process(j, processes.back(), *statement_node); - /* Construct process_ts for job continuations (pipelines), by walking the list until we hit the terminal (empty) job continuation */ + // Construct process_ts for job continuations (pipelines), by walking the list until we hit the + // terminal (empty) job continuation. const parse_node_t *job_cont = get_child(job_node, 1, symbol_job_continuation); assert(job_cont != NULL); - while (result == parse_execution_success && job_cont->child_count > 0) - { + while (result == parse_execution_success && job_cont->child_count > 0) { assert(job_cont->type == symbol_job_continuation); - /* Handle the pipe, whose fd may not be the obvious stdout */ + // Handle the pipe, whose fd may not be the obvious stdout. const parse_node_t &pipe_node = *get_child(*job_cont, 0, parse_token_type_pipe); int pipe_write_fd = fd_redirected_by_pipe(get_source(pipe_node)); - if (pipe_write_fd == -1) - { + if (pipe_write_fd == -1) { result = report_error(pipe_node, ILLEGAL_FD_ERR_MSG, get_source(pipe_node).c_str()); break; } processes.back()->pipe_write_fd = pipe_write_fd; - /* Get the statement node and make a process from it */ + // Get the statement node and make a process from it. const parse_node_t *statement_node = get_child(*job_cont, 1, symbol_statement); assert(statement_node != NULL); - /* Store the new process (and maybe with an error) */ + // Store the new process (and maybe with an error). processes.push_back(new process_t()); result = this->populate_job_process(j, processes.back(), *statement_node); - /* Get the next continuation */ + // Get the next continuation. job_cont = get_child(*job_cont, 2, symbol_job_continuation); assert(job_cont != NULL); } - /* Return what happened */ - if (result == parse_execution_success) - { - /* Link up the processes */ - assert(! processes.empty()); + // Return what happened. + if (result == parse_execution_success) { + // Link up the processes. + assert(!processes.empty()); j->first_process = processes.at(0); - for (size_t i=1 ; i < processes.size(); i++) - { - processes.at(i-1)->next = processes.at(i); + for (size_t i = 1; i < processes.size(); i++) { + processes.at(i - 1)->next = processes.at(i); } - } - else - { - /* Clean up processes */ - for (size_t i=0; i < processes.size(); i++) - { + } else { + // Clean up processes. + for (size_t i = 0; i < processes.size(); i++) { const process_t *proc = processes.at(i); processes.at(i) = NULL; delete proc; @@ -1290,75 +1174,75 @@ parse_execution_result_t parse_execution_context_t::populate_job_from_job_node(j return result; } -parse_execution_result_t parse_execution_context_t::run_1_job(const parse_node_t &job_node, const block_t *associated_block) -{ - if (should_cancel_execution(associated_block)) - { +parse_execution_result_t parse_execution_context_t::run_1_job(const parse_node_t &job_node, + const block_t *associated_block) { + if (should_cancel_execution(associated_block)) { return parse_execution_cancelled; } - // Get terminal modes + // Get terminal modes. struct termios tmodes = {}; - if (get_is_interactive()) - { - if (tcgetattr(STDIN_FILENO, &tmodes)) - { - // need real error handling here + if (get_is_interactive()) { + if (tcgetattr(STDIN_FILENO, &tmodes)) { + // Need real error handling here. wperror(L"tcgetattr"); return parse_execution_errored; } } - /* Increment the eval_level for the duration of this command */ + // Increment the eval_level for the duration of this command. scoped_push saved_eval_level(&eval_level, eval_level + 1); - /* Save the node index */ + // Save the node index. scoped_push saved_node_offset(&executing_node_idx, this->get_offset(job_node)); - /* Profiling support */ + // Profiling support. long long start_time = 0, parse_time = 0, exec_time = 0; profile_item_t *profile_item = this->parser->create_profile_item(); - if (profile_item != NULL) - { + if (profile_item != NULL) { start_time = get_time(); } - /* When we encounter a block construct (e.g. while loop) in the general case, we create a "block process" that has a pointer to its source. This allows us to handle block-level redirections. However, if there are no redirections, then we can just jump into the block directly, which is significantly faster. */ - if (job_is_simple_block(job_node)) - { + // When we encounter a block construct (e.g. while loop) in the general case, we create a "block + // process" that has a pointer to its source. This allows us to handle block-level redirections. + // However, if there are no redirections, then we can just jump into the block directly, which + // is significantly faster. + if (job_is_simple_block(job_node)) { parse_execution_result_t result = parse_execution_success; - + const parse_node_t &statement = *get_child(job_node, 0, symbol_statement); const parse_node_t &specific_statement = *get_child(statement, 0); assert(specific_statement_type_is_redirectable_block(specific_statement)); - switch (specific_statement.type) - { - case symbol_block_statement: + switch (specific_statement.type) { + case symbol_block_statement: { result = this->run_block_statement(specific_statement); break; - - case symbol_if_statement: + } + case symbol_if_statement: { result = this->run_if_statement(specific_statement); break; - - case symbol_switch_statement: + } + case symbol_switch_statement: { result = this->run_switch_statement(specific_statement); break; - - default: - /* Other types should be impossible due to the specific_statement_type_is_redirectable_block check */ + } + default: { + // Other types should be impossible due to the + // specific_statement_type_is_redirectable_block check. PARSER_DIE(); break; + } } - if (profile_item != NULL) - { - /* Block-types profile a little weird. They have no 'parse' time, and their command is just the block type */ + if (profile_item != NULL) { + // Block-types profile a little weird. They have no 'parse' time, and their command is + // just the block type. exec_time = get_time(); - profile_item->level=eval_level; + profile_item->level = eval_level; profile_item->parse = 0; - profile_item->exec=(int)(exec_time-start_time); - profile_item->cmd = profiling_cmd_name_for_redirectable_block(specific_statement, this->tree, this->src); + profile_item->exec = (int)(exec_time - start_time); + profile_item->cmd = profiling_cmd_name_for_redirectable_block(specific_statement, + this->tree, this->src); profile_item->skipped = false; } @@ -1368,27 +1252,27 @@ parse_execution_result_t parse_execution_context_t::run_1_job(const parse_node_t job_t *j = new job_t(acquire_job_id(), block_io); j->tmodes = tmodes; job_set_flag(j, JOB_CONTROL, - (job_control_mode==JOB_CONTROL_ALL) || - ((job_control_mode == JOB_CONTROL_INTERACTIVE) && (get_is_interactive()))); + (job_control_mode == JOB_CONTROL_ALL) || + ((job_control_mode == JOB_CONTROL_INTERACTIVE) && (get_is_interactive()))); - job_set_flag(j, JOB_FOREGROUND, ! tree.job_should_be_backgrounded(job_node)); + job_set_flag(j, JOB_FOREGROUND, !tree.job_should_be_backgrounded(job_node)); - job_set_flag(j, JOB_TERMINAL, - job_get_flag(j, JOB_CONTROL) && !is_subshell && !is_event); + 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()); - /* Tell the current block what its job is. This has to happen before we populate it (#1394) */ + // Tell the current block what its job is. This has to happen before we populate it (#1394). parser->current_block()->job = j; - /* Populate the job. This may fail for reasons like command_not_found. If this fails, an error will have been printed */ - parse_execution_result_t pop_result = this->populate_job_from_job_node(j, job_node, associated_block); + // Populate the job. This may fail for reasons like command_not_found. If this fails, an error + // will have been printed. + parse_execution_result_t pop_result = + this->populate_job_from_job_node(j, job_node, associated_block); - /* Clean up the job on failure or cancellation */ + // Clean up the job on failure or cancellation. bool populated_job = (pop_result == parse_execution_success); - if (! populated_job || this->should_cancel_execution(associated_block)) - { + if (!populated_job || this->should_cancel_execution(associated_block)) { assert(parser->current_block()->job == j); parser->current_block()->job = NULL; delete j; @@ -1396,191 +1280,169 @@ parse_execution_result_t parse_execution_context_t::run_1_job(const parse_node_t populated_job = false; } - - /* Store time it took to 'parse' the command */ - if (profile_item != NULL) - { + // Store time it took to 'parse' the command. + if (profile_item != NULL) { parse_time = get_time(); } - if (populated_job) - { - /* Success. Give the job to the parser - it will clean it up. */ + if (populated_job) { + // Success. Give the job to the parser - it will clean it up. parser->job_add(j); - /* Check to see if this contained any external commands */ + // Check to see if this contained any external commands. bool job_contained_external_command = false; - for (const process_t *proc = j->first_process; proc != NULL; proc = proc->next) - { - if (proc->type == EXTERNAL) - { + for (const process_t *proc = j->first_process; proc != NULL; proc = proc->next) { + if (proc->type == EXTERNAL) { job_contained_external_command = true; break; } } - /* Actually execute the job */ + // Actually execute the job. exec_job(*this->parser, j); - /* Only external commands require a new fishd barrier */ - if (job_contained_external_command) - { + // Only external commands require a new fishd barrier. + if (job_contained_external_command) { set_proc_had_barrier(false); } } - if (profile_item != NULL) - { + if (profile_item != NULL) { exec_time = get_time(); - profile_item->level=eval_level; - profile_item->parse = (int)(parse_time-start_time); - profile_item->exec=(int)(exec_time-parse_time); + profile_item->level = eval_level; + profile_item->parse = (int)(parse_time - start_time); + profile_item->exec = (int)(exec_time - parse_time); profile_item->cmd = j ? j->command() : wcstring(); - profile_item->skipped = ! populated_job; + profile_item->skipped = !populated_job; } - - /* Clean up jobs. */ - job_reap(0); - - /* All done */ + job_reap(0); // clean up jobs return parse_execution_success; } -parse_execution_result_t parse_execution_context_t::run_job_list(const parse_node_t &job_list_node, const block_t *associated_block) -{ +parse_execution_result_t parse_execution_context_t::run_job_list(const parse_node_t &job_list_node, + const block_t *associated_block) { assert(job_list_node.type == symbol_job_list || job_list_node.type == symbol_andor_job_list); parse_execution_result_t result = parse_execution_success; const parse_node_t *job_list = &job_list_node; - while (job_list != NULL && ! should_cancel_execution(associated_block)) - { + while (job_list != NULL && !should_cancel_execution(associated_block)) { assert(job_list->type == symbol_job_list || job_list_node.type == symbol_andor_job_list); - // Try pulling out a job + // Try pulling out a job. const parse_node_t *job = tree.next_node_in_node_list(*job_list, symbol_job, &job_list); - if (job != NULL) - { + if (job != NULL) { result = this->run_1_job(*job, associated_block); } } - /* Returns the last job executed */ + // Returns the last job executed. return result; } -parse_execution_result_t parse_execution_context_t::eval_node_at_offset(node_offset_t offset, const block_t *associated_block, const io_chain_t &io) -{ - /* Don't ever expect to have an empty tree if this is called */ - assert(! tree.empty()); +parse_execution_result_t parse_execution_context_t::eval_node_at_offset( + node_offset_t offset, const block_t *associated_block, const io_chain_t &io) { + // Don't ever expect to have an empty tree if this is called. + assert(!tree.empty()); assert(offset < tree.size()); - /* Apply this block IO for the duration of this function */ + // Apply this block IO for the duration of this function. scoped_push block_io_push(&block_io, io); const parse_node_t &node = tree.at(offset); - /* Currently, we only expect to execute the top level job list, or a block node. Assert that. */ + // Currently, we only expect to execute the top level job list, or a block node. Assert that. assert(node.type == symbol_job_list || specific_statement_type_is_redirectable_block(node)); enum parse_execution_result_t status = parse_execution_success; - switch (node.type) - { - case symbol_job_list: - { - /* We should only get a job list if it's the very first node. This is because this is the entry point for both top-level execution (the first node) and INTERNAL_BLOCK_NODE execution (which does block statements, but never job lists) */ + switch (node.type) { + case symbol_job_list: { + // We should only get a job list if it's the very first node. This is because this is + // the entry point for both top-level execution (the first node) and INTERNAL_BLOCK_NODE + // execution (which does block statements, but never job lists). assert(offset == 0); wcstring func_name; - const parse_node_t *infinite_recursive_node = this->infinite_recursive_statement_in_job_list(node, &func_name); - if (infinite_recursive_node != NULL) - { - /* We have an infinite recursion */ - this->report_error(*infinite_recursive_node, INFINITE_FUNC_RECURSION_ERR_MSG, func_name.c_str()); + const parse_node_t *infinite_recursive_node = + this->infinite_recursive_statement_in_job_list(node, &func_name); + if (infinite_recursive_node != NULL) { + // We have an infinite recursion. + this->report_error(*infinite_recursive_node, INFINITE_FUNC_RECURSION_ERR_MSG, + func_name.c_str()); status = parse_execution_errored; - } - else - { - /* No infinite recursion */ + } else { + // No infinite recursion. status = this->run_job_list(node, associated_block); } break; } - - case symbol_block_statement: + case symbol_block_statement: { status = this->run_block_statement(node); break; - - case symbol_if_statement: + } + case symbol_if_statement: { status = this->run_if_statement(node); break; - - case symbol_switch_statement: + } + case symbol_switch_statement: { status = this->run_switch_statement(node); break; - - default: - /* In principle, we could support other node types. However we never expect to be passed them - see above. */ - fprintf(stderr, "Unexpected node %ls found in %s\n", node.describe().c_str(), __FUNCTION__); + } + default: { + // In principle, we could support other node types. However we never expect to be passed + // them - see above. + fprintf(stderr, "Unexpected node %ls found in %s\n", node.describe().c_str(), + __FUNCTION__); PARSER_DIE(); break; + } } return status; } -int parse_execution_context_t::line_offset_of_node_at_offset(node_offset_t requested_index) -{ - /* If we're not executing anything, return -1 */ - if (requested_index == NODE_OFFSET_INVALID) - { +int parse_execution_context_t::line_offset_of_node_at_offset(node_offset_t requested_index) { + // If we're not executing anything, return -1. + if (requested_index == NODE_OFFSET_INVALID) { return -1; } - /* If for some reason we're executing a node without source, return -1 */ + // If for some reason we're executing a node without source, return -1. const parse_node_t &node = tree.at(requested_index); - if (! node.has_source()) - { + if (!node.has_source()) { return -1; } - + size_t char_offset = tree.at(requested_index).source_start; return this->line_offset_of_character_at_offset(char_offset); } -int parse_execution_context_t::line_offset_of_character_at_offset(size_t offset) -{ - /* Count the number of newlines, leveraging our cache */ +int parse_execution_context_t::line_offset_of_character_at_offset(size_t offset) { + // Count the number of newlines, leveraging our cache. assert(offset <= src.size()); - /* Easy hack to handle 0 */ - if (offset == 0) - { + // Easy hack to handle 0. + if (offset == 0) { return 0; } - /* 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. */ + // 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 = src.c_str(); - if (offset > cached_lineno_offset) - { + if (offset > cached_lineno_offset) { size_t i; - for (i = cached_lineno_offset; str[i] != L'\0' && i < offset; i++) - { - /* Add one for every newline we find in the range [cached_lineno_offset, offset) */ - if (str[i] == L'\n') - { + for (i = cached_lineno_offset; str[i] != L'\0' && i < offset; i++) { + // Add one for every newline we find in the range [cached_lineno_offset, offset). + if (str[i] == L'\n') { cached_lineno_count++; } } - cached_lineno_offset = i; //note: i, not offset, in case offset is beyond the length of the string - } - else if (offset < cached_lineno_offset) - { - /* Subtract one for every newline we find in the range [offset, cached_lineno_offset) */ - for (size_t i = offset; i < cached_lineno_offset; i++) - { - if (str[i] == L'\n') - { + cached_lineno_offset = + i; // note: i, not offset, in case offset is beyond the length of the string + } else if (offset < cached_lineno_offset) { + // Subtract one for every newline we find in the range [offset, cached_lineno_offset). + for (size_t i = offset; i < cached_lineno_offset; i++) { + if (str[i] == L'\n') { cached_lineno_count--; } } @@ -1589,26 +1451,21 @@ int parse_execution_context_t::line_offset_of_character_at_offset(size_t offset) return cached_lineno_count; } -int parse_execution_context_t::get_current_line_number() -{ +int parse_execution_context_t::get_current_line_number() { int line_number = -1; int line_offset = this->line_offset_of_node_at_offset(this->executing_node_idx); - if (line_offset >= 0) - { - /* The offset is 0 based; the number is 1 based */ + if (line_offset >= 0) { + // The offset is 0 based; the number is 1 based. line_number = line_offset + 1; } return line_number; } -int parse_execution_context_t::get_current_source_offset() const -{ +int parse_execution_context_t::get_current_source_offset() const { int result = -1; - if (executing_node_idx != NODE_OFFSET_INVALID) - { + if (executing_node_idx != NODE_OFFSET_INVALID) { const parse_node_t &node = tree.at(executing_node_idx); - if (node.has_source()) - { + if (node.has_source()) { result = static_cast(node.source_start); } } diff --git a/src/parse_execution.h b/src/parse_execution.h index 088729cb8..5c1ee1013 100644 --- a/src/parse_execution.h +++ b/src/parse_execution.h @@ -1,65 +1,55 @@ -/**\file parse_execution.h - - Provides the "linkage" between a parse_node_tree_t and actual execution structures (job_t, etc.). -*/ +// Provides the "linkage" between a parse_node_tree_t and actual execution structures (job_t, etc.). #ifndef FISH_PARSE_EXECUTION_H #define FISH_PARSE_EXECUTION_H #include +#include #include "common.h" #include "io.h" #include "parse_constants.h" #include "parse_tree.h" #include "proc.h" -#include class parser_t; struct block_t; -enum parse_execution_result_t -{ - /* The job was successfully executed (though it have failed on its own). */ +enum parse_execution_result_t { + /// The job was successfully executed (though it have failed on its own). parse_execution_success, - - /* The job did not execute due to some error (e.g. failed to wildcard expand). An error will have been printed and proc_last_status will have been set. */ + /// The job did not execute due to some error (e.g. failed to wildcard expand). An error will + /// have been printed and proc_last_status will have been set. parse_execution_errored, - - /* The job was cancelled (e.g. Ctrl-C) */ + /// The job was cancelled (e.g. Ctrl-C). parse_execution_cancelled, - - /* The job was skipped (e.g. due to a not-taken 'and' command). This is a special return allowed only from the populate functions, not the run functions. */ + /// The job was skipped (e.g. due to a not-taken 'and' command). This is a special return + /// allowed only from the populate functions, not the run functions. parse_execution_skipped }; -class parse_execution_context_t -{ -private: +class parse_execution_context_t { + private: const parse_node_tree_t tree; const wcstring src; io_chain_t block_io; - parser_t * const parser; - //parse_error_list_t errors; - + parser_t *const parser; + // parse_error_list_t errors; int eval_level; - - /* The currently executing node index, used to indicate the line number */ + // The currently executing node index, used to indicate the line number. node_offset_t executing_node_idx; - - /* Cached line number information */ + // Cached line number information. size_t cached_lineno_offset; int cached_lineno_count; + // No copying allowed. + parse_execution_context_t(const parse_execution_context_t &); + parse_execution_context_t &operator=(const parse_execution_context_t &); - /* No copying allowed */ - parse_execution_context_t(const parse_execution_context_t&); - parse_execution_context_t& operator=(const parse_execution_context_t&); - - /* Should I cancel? */ + // Should I cancel? bool should_cancel_execution(const block_t *block) const; - /* Ways that we can stop executing a block. These are in a sort of ascending order of importance, e.g. `exit` should trump `break` */ - enum execution_cancellation_reason_t - { + // Ways that we can stop executing a block. These are in a sort of ascending order of + // importance, e.g. `exit` should trump `break`. + enum execution_cancellation_reason_t { execution_cancellation_none, execution_cancellation_loop_control, execution_cancellation_skip, @@ -67,85 +57,98 @@ class parse_execution_context_t }; execution_cancellation_reason_t cancellation_reason(const block_t *block) const; - /* Report an error. Always returns true. */ + // Report an error. Always returns true. parse_execution_result_t report_error(const parse_node_t &node, const wchar_t *fmt, ...) const; parse_execution_result_t report_errors(const parse_error_list_t &errors) const; - /* Wildcard error helper */ - parse_execution_result_t report_unmatched_wildcard_error(const parse_node_t &unmatched_wildcard); + // Wildcard error helper. + parse_execution_result_t report_unmatched_wildcard_error( + const parse_node_t &unmatched_wildcard); - /* Command not found support */ - parse_execution_result_t handle_command_not_found(const wcstring &cmd, const parse_node_t &statement_node, int err_code); + /// Command not found support. + parse_execution_result_t handle_command_not_found(const wcstring &cmd, + const parse_node_t &statement_node, + int err_code); - /* Utilities */ + // Utilities wcstring get_source(const parse_node_t &node) const; - const parse_node_t *get_child(const parse_node_t &parent, node_offset_t which, parse_token_type_t expected_type = token_type_invalid) const; + const parse_node_t *get_child(const parse_node_t &parent, node_offset_t which, + parse_token_type_t expected_type = token_type_invalid) const; node_offset_t get_offset(const parse_node_t &node) const; - const parse_node_t *infinite_recursive_statement_in_job_list(const parse_node_t &job_list, wcstring *out_func_name) const; + const parse_node_t *infinite_recursive_statement_in_job_list(const parse_node_t &job_list, + wcstring *out_func_name) const; - /* Indicates whether a job is a simple block (one block, no redirections) */ + /// Indicates whether a job is a simple block (one block, no redirections). bool job_is_simple_block(const parse_node_t &node) const; - enum process_type_t process_type_for_command(const parse_node_t &plain_statement, const wcstring &cmd) const; + enum process_type_t process_type_for_command(const parse_node_t &plain_statement, + const wcstring &cmd) const; - /* These create process_t structures from statements */ - parse_execution_result_t populate_job_process(job_t *job, process_t *proc, const parse_node_t &statement_node); - parse_execution_result_t populate_boolean_process(job_t *job, process_t *proc, const parse_node_t &bool_statement); - parse_execution_result_t populate_plain_process(job_t *job, process_t *proc, const parse_node_t &statement); - parse_execution_result_t populate_block_process(job_t *job, process_t *proc, const parse_node_t &statement_node); + // These create process_t structures from statements. + parse_execution_result_t populate_job_process(job_t *job, process_t *proc, + const parse_node_t &statement_node); + parse_execution_result_t populate_boolean_process(job_t *job, process_t *proc, + const parse_node_t &bool_statement); + parse_execution_result_t populate_plain_process(job_t *job, process_t *proc, + const parse_node_t &statement); + parse_execution_result_t populate_block_process(job_t *job, process_t *proc, + const parse_node_t &statement_node); - /* These encapsulate the actual logic of various (block) statements. */ + // These encapsulate the actual logic of various (block) statements. parse_execution_result_t run_block_statement(const parse_node_t &statement); - parse_execution_result_t run_for_statement(const parse_node_t &header, const parse_node_t &contents); + parse_execution_result_t run_for_statement(const parse_node_t &header, + const parse_node_t &contents); parse_execution_result_t run_if_statement(const parse_node_t &statement); parse_execution_result_t run_switch_statement(const parse_node_t &statement); - parse_execution_result_t run_while_statement(const parse_node_t &header, const parse_node_t &contents); - parse_execution_result_t run_function_statement(const parse_node_t &header, const parse_node_t &block_end_command); - parse_execution_result_t run_begin_statement(const parse_node_t &header, const parse_node_t &contents); + parse_execution_result_t run_while_statement(const parse_node_t &header, + const parse_node_t &contents); + parse_execution_result_t run_function_statement(const parse_node_t &header, + const parse_node_t &block_end_command); + parse_execution_result_t run_begin_statement(const parse_node_t &header, + const parse_node_t &contents); - enum globspec_t - { - failglob, - nullglob - }; - parse_execution_result_t determine_arguments(const parse_node_t &parent, wcstring_list_t *out_arguments, globspec_t glob_behavior); + enum globspec_t { failglob, nullglob }; + parse_execution_result_t determine_arguments(const parse_node_t &parent, + wcstring_list_t *out_arguments, + globspec_t glob_behavior); - /* Determines the IO chain. Returns true on success, false on error */ + // Determines the IO chain. Returns true on success, false on error. bool determine_io_chain(const parse_node_t &statement, io_chain_t *out_chain); - parse_execution_result_t run_1_job(const parse_node_t &job_node, const block_t *associated_block); - parse_execution_result_t run_job_list(const parse_node_t &job_list_node, const block_t *associated_block); - parse_execution_result_t populate_job_from_job_node(job_t *j, const parse_node_t &job_node, const block_t *associated_block); + parse_execution_result_t run_1_job(const parse_node_t &job_node, + const block_t *associated_block); + parse_execution_result_t run_job_list(const parse_node_t &job_list_node, + const block_t *associated_block); + parse_execution_result_t populate_job_from_job_node(job_t *j, const parse_node_t &job_node, + const block_t *associated_block); - /* Returns the line number of the node at the given index, indexed from 0. Not const since it touches cached_lineno_offset */ + // Returns the line number of the node at the given index, indexed from 0. Not const since it + // touches cached_lineno_offset. int line_offset_of_node_at_offset(node_offset_t idx); int line_offset_of_character_at_offset(size_t char_idx); -public: - parse_execution_context_t(moved_ref t, const wcstring &s, parser_t *p, int initial_eval_level); + public: + parse_execution_context_t(moved_ref t, const wcstring &s, parser_t *p, + int initial_eval_level); - /* Returns the current eval level */ - int current_eval_level() const - { - return eval_level; - } + /// Returns the current eval level. + int current_eval_level() const { return eval_level; } - /* Returns the current line number, indexed from 1. Not const since it touches cached_lineno_offset */ + /// Returns the current line number, indexed from 1. Not const since it touches + /// cached_lineno_offset. int get_current_line_number(); - /* Returns the source offset, or -1 */ + /// Returns the source offset, or -1. int get_current_source_offset() const; - /* Returns the source string */ - const wcstring &get_source() const - { - return src; - } - - /* Start executing at the given node offset. Returns 0 if there was no error, 1 if there was an error */ - parse_execution_result_t eval_node_at_offset(node_offset_t offset, const block_t *associated_block, const io_chain_t &io); + /// Returns the source string. + const wcstring &get_source() const { return src; } + /// Start executing at the given node offset. Returns 0 if there was no error, 1 if there was an + /// error. + parse_execution_result_t eval_node_at_offset(node_offset_t offset, + const block_t *associated_block, + const io_chain_t &io); }; - #endif From 3d6f995a9c07a50edd62ac61f7d6b400fa4721ec Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Mon, 2 May 2016 15:37:29 -0700 Subject: [PATCH 198/363] remove inadvertently added key_reader binary --- key_reader | Bin 1470480 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100755 key_reader diff --git a/key_reader b/key_reader deleted file mode 100755 index 96f519eae333c4f02ca3d3032d46ba9ee37151d4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1470480 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%0AV@j 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
    #3hi!p#}C-n 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 z%0A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 ) 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

    Uby3A+en8Fj-v=;lZWn2U3=kYq663&5 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

    mYy0CNC{VN9yoh4$PVp&WS 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 From 61a614b61210ebb65f0a0c9af765d9ba8105e311 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Mon, 2 May 2016 15:48:57 -0700 Subject: [PATCH 199/363] restyle parse_productions module to match project style Reduces lint errors from 33 to 9 (-73%). Line count from 551 to 534 (-3%). Another step in resolving issue #2902. --- src/parse_productions.cpp | 540 +++++++++++++++++++------------------- src/parse_productions.h | 53 ++-- 2 files changed, 288 insertions(+), 305 deletions(-) diff --git a/src/parse_productions.cpp b/src/parse_productions.cpp index 43a580d30..bb5b0e5fb 100644 --- a/src/parse_productions.cpp +++ b/src/parse_productions.cpp @@ -1,488 +1,482 @@ #include -#include "parse_tree.h" -#include "parse_productions.h" -#include "parse_constants.h" #include "common.h" +#include "parse_constants.h" +#include "parse_productions.h" +#include "parse_tree.h" using namespace parse_productions; #define NO_PRODUCTION NULL -/* Herein are encoded the productions for our LL2 fish grammar. - Each symbol (e.g. symbol_job_list) has a corresponding function (e.g. resolve_job_lits). - The function accepts two tokens, representing the first and second lookahead, and returns - returns a production representing the rule, or NULL on error. There is also a tag value - which is returned by reference; the tag is a sort of node annotation. - - Productions are generally a static const array, and we return a pointer to the array (yes, really). - */ +// Herein are encoded the productions for our LL2 fish grammar. +// +// Each symbol (e.g. symbol_job_list) has a corresponding function (e.g. resolve_job_lits). The +// function accepts two tokens, representing the first and second lookahead, and returns returns a +// production representing the rule, or NULL on error. There is also a tag value which is returned +// by reference; the tag is a sort of node annotation. +// +// Productions are generally a static const array, and we return a pointer to the array (yes, +// really). -#define RESOLVE(sym) static const production_t * resolve_##sym (const parse_token_t &token1, const parse_token_t &token2, parse_node_tag_t *out_tag) +#define RESOLVE(sym) \ + static const production_t *resolve_##sym( \ + const parse_token_t &token1, const parse_token_t &token2, parse_node_tag_t *out_tag) -/* Hacktastic? */ -#define RESOLVE_ONLY(sym) \ - extern const production_t sym##_only; \ - static const production_t * resolve_##sym (const parse_token_t &token1, const parse_token_t &token2, parse_node_tag_t *out_tag) { return &sym ## _only ; } \ - const production_t sym ## _only +// Hacktastic? +#define RESOLVE_ONLY(sym) \ + extern const production_t sym##_only; \ + static const production_t *resolve_##sym( \ + const parse_token_t &token1, const parse_token_t &token2, parse_node_tag_t *out_tag) { \ + return &sym##_only; \ + } \ + const production_t sym##_only #define KEYWORD(x) ((x) + LAST_TOKEN_OR_SYMBOL + 1) -/* Helper macro to define an array */ +/// Helper macro to define an array. #define P static const production_t -/* A job_list is a list of jobs, separated by semicolons or newlines */ -RESOLVE(job_list) -{ +/// A job_list is a list of jobs, separated by semicolons or newlines. +RESOLVE(job_list) { P list_end = {}; P normal = {symbol_job, symbol_job_list}; P empty_line = {parse_token_type_end, symbol_job_list}; - switch (token1.type) - { - case parse_token_type_string: - // some keywords are special - switch (token1.keyword) - { + switch (token1.type) { + case parse_token_type_string: { + // Some keywords are special. + switch (token1.keyword) { case parse_keyword_end: case parse_keyword_else: - case parse_keyword_case: - // End this job list - return &list_end; - - default: - // Normal string - return &normal; + case parse_keyword_case: { + return &list_end; // end this job list + } + default: { + return &normal; // normal string + } } - + } case parse_token_type_pipe: case parse_token_type_redirection: - case parse_token_type_background: + case parse_token_type_background: { return &normal; - - case parse_token_type_end: + } + case parse_token_type_end: { return &empty_line; - - case parse_token_type_terminate: - // no more commands, just transition to empty - return &list_end; - - default: - return NO_PRODUCTION; + } + case parse_token_type_terminate: { + return &list_end; // no more commands, just transition to empty + } + default: { return NO_PRODUCTION; } } } -/* A job is a non-empty list of statements, separated by pipes. (Non-empty is useful for cases like if statements, where we require a command). To represent "non-empty", we require a statement, followed by a possibly empty job_continuation */ +// A job is a non-empty list of statements, separated by pipes. (Non-empty is useful for cases like +// if statements, where we require a command). To represent "non-empty", we require a statement, +// followed by a possibly empty job_continuation. RESOLVE_ONLY(job) = {symbol_statement, symbol_job_continuation, symbol_optional_background}; -RESOLVE(job_continuation) -{ +RESOLVE(job_continuation) { P empty = {}; P piped = {parse_token_type_pipe, symbol_statement, symbol_job_continuation}; - switch (token1.type) - { - case parse_token_type_pipe: - // Pipe, continuation - return &piped; - - default: - // Not a pipe, no job continuation - return ∅ + switch (token1.type) { + case parse_token_type_pipe: { + return &piped; // pipe, continuation + } + default: { + return ∅ // not a pipe, no job continuation + } } } -/* A statement is a normal command, or an if / while / and etc */ -RESOLVE(statement) -{ +// A statement is a normal command, or an if / while / and etc. +RESOLVE(statement) { P boolean = {symbol_boolean_statement}; P block = {symbol_block_statement}; P ifs = {symbol_if_statement}; P switchs = {symbol_switch_statement}; P decorated = {symbol_decorated_statement}; - /* The only block-like builtin that takes any parameters is 'function' So go to decorated statements if the subsequent token looks like '--'. - The logic here is subtle: - If we are 'begin', then we expect to be invoked with no arguments. - If we are 'function', then we are a non-block if we are invoked with -h or --help - If we are anything else, we require an argument, so do the same thing if the subsequent token is a statement terminator. - */ - - if (token1.type == parse_token_type_string) - { - // If we are a function, then look for help arguments - // Otherwise, if the next token looks like an option (starts with a dash), then parse it as a decorated statement - if (token1.keyword == parse_keyword_function && token2.is_help_argument) - { + // The only block-like builtin that takes any parameters is 'function' So go to decorated + // statements if the subsequent token looks like '--'. The logic here is subtle: + // + // If we are 'begin', then we expect to be invoked with no arguments. + // If we are 'function', then we are a non-block if we are invoked with -h or --help + // If we are anything else, we require an argument, so do the same thing if the subsequent token + // is a statement terminator. + if (token1.type == parse_token_type_string) { + // If we are a function, then look for help arguments. Otherwise, if the next token looks + // like an option (starts with a dash), then parse it as a decorated statement. + if (token1.keyword == parse_keyword_function && token2.is_help_argument) { return &decorated; - } - else if (token1.keyword != parse_keyword_function && token2.has_dash_prefix) - { + } else if (token1.keyword != parse_keyword_function && token2.has_dash_prefix) { return &decorated; } - // Likewise if the next token doesn't look like an argument at all. This corresponds to e.g. a "naked if". - bool naked_invocation_invokes_help = (token1.keyword != parse_keyword_begin && token1.keyword != parse_keyword_end); - if (naked_invocation_invokes_help && (token2.type == parse_token_type_end || token2.type == parse_token_type_terminate)) - { + // Likewise if the next token doesn't look like an argument at all. This corresponds to e.g. + // a "naked if". + bool naked_invocation_invokes_help = + (token1.keyword != parse_keyword_begin && token1.keyword != parse_keyword_end); + if (naked_invocation_invokes_help && + (token2.type == parse_token_type_end || token2.type == parse_token_type_terminate)) { return &decorated; } - } - switch (token1.type) - { - case parse_token_type_string: - switch (token1.keyword) - { + switch (token1.type) { + case parse_token_type_string: { + switch (token1.keyword) { case parse_keyword_and: case parse_keyword_or: - case parse_keyword_not: + case parse_keyword_not: { return &boolean; - + } case parse_keyword_for: case parse_keyword_while: case parse_keyword_function: - case parse_keyword_begin: + case parse_keyword_begin: { return █ - - case parse_keyword_if: + } + case parse_keyword_if: { return &ifs; - - case parse_keyword_else: + } + case parse_keyword_else: { return NO_PRODUCTION; - - case parse_keyword_switch: + } + case parse_keyword_switch: { return &switchs; - - case parse_keyword_end: + } + case parse_keyword_end: { return NO_PRODUCTION; - - // All other keywords fall through to decorated statement - default: - return &decorated; + } + // All other keywords fall through to decorated statement. + default: { return &decorated; } } break; - + } case parse_token_type_pipe: case parse_token_type_redirection: case parse_token_type_background: - case parse_token_type_terminate: - return NO_PRODUCTION; - //parse_error(L"statement", token); - - default: + case parse_token_type_terminate: { return NO_PRODUCTION; + // parse_error(L"statement", token); + } + default: { return NO_PRODUCTION; } } } -RESOLVE_ONLY(if_statement) = {symbol_if_clause, symbol_else_clause, symbol_end_command, symbol_arguments_or_redirections_list}; -RESOLVE_ONLY(if_clause) = { KEYWORD(parse_keyword_if), symbol_job, parse_token_type_end, symbol_andor_job_list, symbol_job_list }; +RESOLVE_ONLY(if_statement) = {symbol_if_clause, symbol_else_clause, symbol_end_command, + symbol_arguments_or_redirections_list}; +RESOLVE_ONLY(if_clause) = {KEYWORD(parse_keyword_if), symbol_job, parse_token_type_end, + symbol_andor_job_list, symbol_job_list}; -RESOLVE(else_clause) -{ +RESOLVE(else_clause) { P empty = {}; - P else_cont = { KEYWORD(parse_keyword_else), symbol_else_continuation }; - switch (token1.keyword) - { - case parse_keyword_else: + P else_cont = {KEYWORD(parse_keyword_else), symbol_else_continuation}; + switch (token1.keyword) { + case parse_keyword_else: { return &else_cont; - default: - return ∅ + } + default: { return ∅ } } } -RESOLVE(else_continuation) -{ +RESOLVE(else_continuation) { P elseif = {symbol_if_clause, symbol_else_clause}; P elseonly = {parse_token_type_end, symbol_job_list}; - switch (token1.keyword) - { - case parse_keyword_if: + switch (token1.keyword) { + case parse_keyword_if: { return &elseif; - default: - return &elseonly; + } + default: { return &elseonly; } } } -RESOLVE_ONLY(switch_statement) = { KEYWORD(parse_keyword_switch), symbol_argument, parse_token_type_end, symbol_case_item_list, symbol_end_command, symbol_arguments_or_redirections_list}; +RESOLVE_ONLY(switch_statement) = { + KEYWORD(parse_keyword_switch), symbol_argument, parse_token_type_end, + symbol_case_item_list, symbol_end_command, symbol_arguments_or_redirections_list}; -RESOLVE(case_item_list) -{ +RESOLVE(case_item_list) { P empty = {}; P case_item = {symbol_case_item, symbol_case_item_list}; P blank_line = {parse_token_type_end, symbol_case_item_list}; - if (token1.keyword == parse_keyword_case) return &case_item; - else if (token1.type == parse_token_type_end) return &blank_line; - else return ∅ + if (token1.keyword == parse_keyword_case) + return &case_item; + else if (token1.type == parse_token_type_end) + return &blank_line; + else + return ∅ } -RESOLVE_ONLY(case_item) = {KEYWORD(parse_keyword_case), symbol_argument_list, parse_token_type_end, symbol_job_list}; +RESOLVE_ONLY(case_item) = {KEYWORD(parse_keyword_case), symbol_argument_list, parse_token_type_end, + symbol_job_list}; -RESOLVE(andor_job_list) -{ +RESOLVE(andor_job_list) { P list_end = {}; P andor_job = {symbol_job, symbol_andor_job_list}; P empty_line = {parse_token_type_end, symbol_andor_job_list}; - - if (token1.type == parse_token_type_end) - { + + if (token1.type == parse_token_type_end) { return &empty_line; - } - else if (token1.keyword == parse_keyword_and || token1.keyword == parse_keyword_or) - { - // Check that the argument to and/or is a string that's not help - // Otherwise it's either 'and --help' or a naked 'and', and not part of this list - if (token2.type == parse_token_type_string && !token2.is_help_argument) - { + } else if (token1.keyword == parse_keyword_and || token1.keyword == parse_keyword_or) { + // Check that the argument to and/or is a string that's not help. Otherwise it's either 'and + // --help' or a naked 'and', and not part of this list. + if (token2.type == parse_token_type_string && !token2.is_help_argument) { return &andor_job; } } - // All other cases end the list + // All other cases end the list. return &list_end; } -RESOLVE(argument_list) -{ +RESOLVE(argument_list) { P empty = {}; P arg = {symbol_argument, symbol_argument_list}; - switch (token1.type) - { - case parse_token_type_string: + switch (token1.type) { + case parse_token_type_string: { return &arg; - default: - return ∅ + } + default: { return ∅ } } } -RESOLVE(freestanding_argument_list) -{ +RESOLVE(freestanding_argument_list) { P empty = {}; P arg = {symbol_argument, symbol_freestanding_argument_list}; P semicolon = {parse_token_type_end, symbol_freestanding_argument_list}; - switch (token1.type) - { - case parse_token_type_string: + switch (token1.type) { + case parse_token_type_string: { return &arg; - case parse_token_type_end: + } + case parse_token_type_end: { return &semicolon; - default: - return ∅ + } + default: { return ∅ } } } -RESOLVE_ONLY(block_statement) = {symbol_block_header, symbol_job_list, symbol_end_command, symbol_arguments_or_redirections_list}; +RESOLVE_ONLY(block_statement) = {symbol_block_header, symbol_job_list, symbol_end_command, + symbol_arguments_or_redirections_list}; -RESOLVE(block_header) -{ +RESOLVE(block_header) { P forh = {symbol_for_header}; P whileh = {symbol_while_header}; P funch = {symbol_function_header}; P beginh = {symbol_begin_header}; - switch (token1.keyword) - { - case parse_keyword_for: + switch (token1.keyword) { + case parse_keyword_for: { return &forh; - case parse_keyword_while: + } + case parse_keyword_while: { return &whileh; - case parse_keyword_function: + } + case parse_keyword_function: { return &funch; - case parse_keyword_begin: + } + case parse_keyword_begin: { return &beginh; - default: - return NO_PRODUCTION; + } + default: { return NO_PRODUCTION; } } } -RESOLVE_ONLY(for_header) = {KEYWORD(parse_keyword_for), parse_token_type_string, KEYWORD - (parse_keyword_in), symbol_argument_list, parse_token_type_end}; -RESOLVE_ONLY(while_header) = {KEYWORD(parse_keyword_while), symbol_job, parse_token_type_end, symbol_andor_job_list}; +RESOLVE_ONLY(for_header) = {KEYWORD(parse_keyword_for), parse_token_type_string, + KEYWORD(parse_keyword_in), symbol_argument_list, parse_token_type_end}; +RESOLVE_ONLY(while_header) = {KEYWORD(parse_keyword_while), symbol_job, parse_token_type_end, + symbol_andor_job_list}; RESOLVE_ONLY(begin_header) = {KEYWORD(parse_keyword_begin)}; -RESOLVE_ONLY(function_header) = {KEYWORD(parse_keyword_function), symbol_argument, symbol_argument_list, parse_token_type_end}; +RESOLVE_ONLY(function_header) = {KEYWORD(parse_keyword_function), symbol_argument, + symbol_argument_list, parse_token_type_end}; -/* A boolean statement is AND or OR or NOT */ -RESOLVE(boolean_statement) -{ +// A boolean statement is AND or OR or NOT. +RESOLVE(boolean_statement) { P ands = {KEYWORD(parse_keyword_and), symbol_statement}; P ors = {KEYWORD(parse_keyword_or), symbol_statement}; P nots = {KEYWORD(parse_keyword_not), symbol_statement}; - switch (token1.keyword) - { - case parse_keyword_and: + switch (token1.keyword) { + case parse_keyword_and: { *out_tag = parse_bool_and; return &ands; - case parse_keyword_or: + } + case parse_keyword_or: { *out_tag = parse_bool_or; return &ors; - case parse_keyword_not: + } + case parse_keyword_not: { *out_tag = parse_bool_not; return ¬s; - default: - return NO_PRODUCTION; + } + default: { return NO_PRODUCTION; } } } -RESOLVE(decorated_statement) -{ +RESOLVE(decorated_statement) { P plains = {symbol_plain_statement}; P cmds = {KEYWORD(parse_keyword_command), symbol_plain_statement}; P builtins = {KEYWORD(parse_keyword_builtin), symbol_plain_statement}; P execs = {KEYWORD(parse_keyword_exec), symbol_plain_statement}; - /* If this is e.g. 'command --help' then the command is 'command' and not a decoration. If the second token is not a string, then this is a naked 'command' and we should execute it as undecorated. */ - if (token2.type != parse_token_type_string || token2.has_dash_prefix) - { + // If this is e.g. 'command --help' then the command is 'command' and not a decoration. If the + // second token is not a string, then this is a naked 'command' and we should execute it as + // undecorated. + if (token2.type != parse_token_type_string || token2.has_dash_prefix) { return &plains; } - switch (token1.keyword) - { - default: - *out_tag = parse_statement_decoration_none; - return &plains; - case parse_keyword_command: + switch (token1.keyword) { + case parse_keyword_command: { *out_tag = parse_statement_decoration_command; return &cmds; - case parse_keyword_builtin: + } + case parse_keyword_builtin: { *out_tag = parse_statement_decoration_builtin; return &builtins; - case parse_keyword_exec: + } + case parse_keyword_exec: { *out_tag = parse_statement_decoration_exec; return &execs; + } + default: { + *out_tag = parse_statement_decoration_none; + return &plains; + } } } RESOLVE_ONLY(plain_statement) = {parse_token_type_string, symbol_arguments_or_redirections_list}; -RESOLVE(arguments_or_redirections_list) -{ +RESOLVE(arguments_or_redirections_list) { P empty = {}; P value = {symbol_argument_or_redirection, symbol_arguments_or_redirections_list}; - switch (token1.type) - { + switch (token1.type) { case parse_token_type_string: - case parse_token_type_redirection: + case parse_token_type_redirection: { return &value; - default: - return ∅ + } + default: { return ∅ } } } -RESOLVE(argument_or_redirection) -{ +RESOLVE(argument_or_redirection) { P arg = {symbol_argument}; P redir = {symbol_redirection}; - switch (token1.type) - { - case parse_token_type_string: + switch (token1.type) { + case parse_token_type_string: { return &arg; - case parse_token_type_redirection: + } + case parse_token_type_redirection: { return &redir; - default: - return NO_PRODUCTION; + } + default: { return NO_PRODUCTION; } } } RESOLVE_ONLY(argument) = {parse_token_type_string}; RESOLVE_ONLY(redirection) = {parse_token_type_redirection, parse_token_type_string}; -RESOLVE(optional_background) -{ +RESOLVE(optional_background) { P empty = {}; - P background = { parse_token_type_background }; - switch (token1.type) - { - case parse_token_type_background: + P background = {parse_token_type_background}; + switch (token1.type) { + case parse_token_type_background: { *out_tag = parse_background; return &background; - default: + } + default: { *out_tag = parse_no_background; return ∅ + } } } RESOLVE_ONLY(end_command) = {KEYWORD(parse_keyword_end)}; -#define TEST(sym) case (symbol_##sym): resolver = resolve_ ## sym ; break; -const production_t *parse_productions::production_for_token(parse_token_type_t node_type, const parse_token_t &input1, const parse_token_t &input2, parse_node_tag_t *out_tag) -{ +#define TEST(sym) \ + case (symbol_##sym): \ + resolver = resolve_##sym; \ + break; +const production_t *parse_productions::production_for_token(parse_token_type_t node_type, + const parse_token_t &input1, + const parse_token_t &input2, + parse_node_tag_t *out_tag) { const bool log_it = false; - if (log_it) - { - fprintf(stderr, "Resolving production for %ls with input token <%ls>\n", token_type_description(node_type), input1.describe().c_str()); + if (log_it) { + fprintf(stderr, "Resolving production for %ls with input token <%ls>\n", + token_type_description(node_type), input1.describe().c_str()); } - /* Fetch the function to resolve the list of productions */ - const production_t * (*resolver)(const parse_token_t &input1, const parse_token_t &input2, parse_node_tag_t *out_tag) = NULL; - switch (node_type) - { - TEST(job_list) - TEST(job) - TEST(statement) - TEST(job_continuation) - TEST(boolean_statement) - TEST(block_statement) - TEST(if_statement) - TEST(if_clause) - TEST(else_clause) - TEST(else_continuation) - TEST(switch_statement) - TEST(decorated_statement) - TEST(case_item_list) - TEST(case_item) - TEST(argument_list) - TEST(freestanding_argument_list) - TEST(block_header) - TEST(for_header) - TEST(while_header) - TEST(begin_header) - TEST(function_header) - TEST(plain_statement) - TEST(andor_job_list) - TEST(arguments_or_redirections_list) - TEST(argument_or_redirection) - TEST(argument) - TEST(redirection) - TEST(optional_background) - TEST(end_command) + // Fetch the function to resolve the list of productions. + const production_t *(*resolver)(const parse_token_t &input1, const parse_token_t &input2, + parse_node_tag_t *out_tag) = NULL; + switch (node_type) { + TEST(job_list) + TEST(job) + TEST(statement) + TEST(job_continuation) + TEST(boolean_statement) + TEST(block_statement) + TEST(if_statement) + TEST(if_clause) + TEST(else_clause) + TEST(else_continuation) + TEST(switch_statement) + TEST(decorated_statement) + TEST(case_item_list) + TEST(case_item) + TEST(argument_list) + TEST(freestanding_argument_list) + TEST(block_header) + TEST(for_header) + TEST(while_header) + TEST(begin_header) + TEST(function_header) + TEST(plain_statement) + TEST(andor_job_list) + TEST(arguments_or_redirections_list) + TEST(argument_or_redirection) + TEST(argument) + TEST(redirection) + TEST(optional_background) + TEST(end_command) case parse_token_type_string: case parse_token_type_pipe: case parse_token_type_redirection: case parse_token_type_background: case parse_token_type_end: - case parse_token_type_terminate: - fprintf(stderr, "Terminal token type %ls passed to %s\n", token_type_description(node_type), __FUNCTION__); + case parse_token_type_terminate: { + fprintf(stderr, "Terminal token type %ls passed to %s\n", + token_type_description(node_type), __FUNCTION__); PARSER_DIE(); break; - + } case parse_special_type_parse_error: case parse_special_type_tokenizer_error: - case parse_special_type_comment: - fprintf(stderr, "Special type %ls passed to %s\n", token_type_description(node_type), __FUNCTION__); + case parse_special_type_comment: { + fprintf(stderr, "Special type %ls passed to %s\n", token_type_description(node_type), + __FUNCTION__); PARSER_DIE(); break; - - - case token_type_invalid: + } + case token_type_invalid: { fprintf(stderr, "token_type_invalid passed to %s\n", __FUNCTION__); PARSER_DIE(); break; - + } } PARSE_ASSERT(resolver != NULL); const production_t *result = resolver(input1, input2, out_tag); - if (result == NULL) - { - if (log_it) - { - fprintf(stderr, "Node type '%ls' has no production for input '%ls' (in %s)\n", token_type_description(node_type), input1.describe().c_str(), __FUNCTION__); + if (result == NULL) { + if (log_it) { + fprintf(stderr, "Node type '%ls' has no production for input '%ls' (in %s)\n", + token_type_description(node_type), input1.describe().c_str(), __FUNCTION__); } } return result; } - diff --git a/src/parse_productions.h b/src/parse_productions.h index e2c2dcc30..38a505d2c 100644 --- a/src/parse_productions.h +++ b/src/parse_productions.h @@ -1,63 +1,52 @@ -/**\file parse_tree.h - - Programmatic representation of fish code. -*/ +// Programmatic representation of fish code. #ifndef FISH_PARSE_TREE_CONSTRUCTION_H #define FISH_PARSE_TREE_CONSTRUCTION_H -#include #include +#include #include "parse_constants.h" struct parse_token_t; -namespace parse_productions -{ +namespace parse_productions { #define MAX_SYMBOLS_PER_PRODUCTION 6 -/* A production is an array of unsigned char. Symbols are encoded directly as their symbol value. Keywords are encoded with an offset of LAST_TOKEN_OR_SYMBOL + 1. So essentially we glom together keywords and symbols. */ +// A production is an array of unsigned char. Symbols are encoded directly as their symbol value. +// Keywords are encoded with an offset of LAST_TOKEN_OR_SYMBOL + 1. So essentially we glom together +// keywords and symbols. typedef uint8_t production_element_t; typedef production_element_t const production_t[MAX_SYMBOLS_PER_PRODUCTION]; -/* Resolve the type from a production element */ -inline parse_token_type_t production_element_type(production_element_t elem) -{ - if (elem > LAST_TOKEN_OR_SYMBOL) - { +/// Resolve the type from a production element. +inline parse_token_type_t production_element_type(production_element_t elem) { + if (elem > LAST_TOKEN_OR_SYMBOL) { return parse_token_type_string; - } - else - { + } else { return static_cast(elem); } } -/* Resolve the keyword from a production element */ -inline parse_keyword_t production_element_keyword(production_element_t elem) -{ - if (elem > LAST_TOKEN_OR_SYMBOL) - { - // First keyword is LAST_TOKEN_OR_SYMBOL + 1 +/// Resolve the keyword from a production element. +inline parse_keyword_t production_element_keyword(production_element_t elem) { + if (elem > LAST_TOKEN_OR_SYMBOL) { + // First keyword is LAST_TOKEN_OR_SYMBOL + 1. return static_cast(elem - LAST_TOKEN_OR_SYMBOL - 1); - } - else - { + } else { return parse_keyword_none; } } -/* Check if an element is valid */ -inline bool production_element_is_valid(production_element_t elem) -{ +/// Check if an element is valid. +inline bool production_element_is_valid(production_element_t elem) { return elem != token_type_invalid; } -/* Fetch a production. We are passed two input tokens. The first input token is guaranteed to not be invalid; the second token may be invalid if there's no more tokens. We may also set flags. */ -const production_t *production_for_token(parse_token_type_t node_type, const parse_token_t &input1, const parse_token_t &input2, uint8_t *out_tag); - +/// Fetch a production. We are passed two input tokens. The first input token is guaranteed to not +/// be invalid; the second token may be invalid if there's no more tokens. We may also set flags. +const production_t *production_for_token(parse_token_type_t node_type, const parse_token_t &input1, + const parse_token_t &input2, uint8_t *out_tag); } - #endif From 716706bd9f297e7fde72d8ddaaf67beadc4638f2 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Mon, 2 May 2016 16:09:46 -0700 Subject: [PATCH 200/363] restyle parse_tree module to match project style Reduces lint errors from 163 to 52 (-68%). Line count from 2012 to 1904 (-5%). Another step in resolving issue #2902. --- src/parse_tree.cpp | 1458 ++++++++++++++++++++------------------------ src/parse_tree.h | 414 ++++++------- 2 files changed, 882 insertions(+), 990 deletions(-) diff --git a/src/parse_tree.cpp b/src/parse_tree.cpp index 0c2334ec5..ef79082f3 100644 --- a/src/parse_tree.cpp +++ b/src/parse_tree.cpp @@ -1,24 +1,25 @@ +// Programmatic representation of fish code. #include #include #include #include #include -#include -#include #include #include +#include +#include #include "common.h" +#include "fallback.h" // IWYU pragma: keep #include "parse_constants.h" #include "parse_productions.h" #include "parse_tree.h" -#include "tokenizer.h" -#include "fallback.h" // IWYU pragma: keep -#include "wutil.h" // IWYU pragma: keep #include "proc.h" +#include "tokenizer.h" +#include "wutil.h" // IWYU pragma: keep // This array provides strings for each symbol in enum parse_token_type_t in parse_constants.h. -const wchar_t * const token_type_map[] = { +const wchar_t *const token_type_map[] = { L"token_type_invalid", L"symbol_job_list", L"symbol_job", @@ -58,79 +59,73 @@ const wchar_t * const token_type_map[] = { L"parse_special_type_parse_error", L"parse_special_type_tokenizer_error", L"parse_special_type_comment", - }; +}; using namespace parse_productions; -static bool production_is_empty(const production_t *production) -{ +static bool production_is_empty(const production_t *production) { return (*production)[0] == token_type_invalid; } -/** Returns a string description of this parse error */ -wcstring parse_error_t::describe_with_prefix(const wcstring &src, const wcstring &prefix, bool is_interactive, bool skip_caret) const -{ +/// Returns a string description of this parse error. +wcstring parse_error_t::describe_with_prefix(const wcstring &src, const wcstring &prefix, + bool is_interactive, bool skip_caret) const { wcstring result = text; - if (! skip_caret && source_start < src.size() && source_start + source_length <= src.size()) - { - // Locate the beginning of this line of source + if (!skip_caret && source_start < src.size() && source_start + source_length <= src.size()) { + // Locate the beginning of this line of source. size_t line_start = 0; - // Look for a newline prior to source_start. If we don't find one, start at the beginning of the string; otherwise start one past the newline. Note that source_start may itself point at a newline; we want to find the newline before it. - if (source_start > 0) - { + // Look for a newline prior to source_start. If we don't find one, start at the beginning of + // the string; otherwise start one past the newline. Note that source_start may itself point + // at a newline; we want to find the newline before it. + if (source_start > 0) { size_t newline = src.find_last_of(L'\n', source_start - 1); - if (newline != wcstring::npos) - { + if (newline != wcstring::npos) { line_start = newline + 1; } } - // Look for the newline after the source range. If the source range itself includes a newline, that's the one we want, so start just before the end of the range - size_t last_char_in_range = (source_length == 0 ? source_start : source_start + source_length - 1); + // Look for the newline after the source range. If the source range itself includes a + // newline, that's the one we want, so start just before the end of the range. + size_t last_char_in_range = + (source_length == 0 ? source_start : source_start + source_length - 1); size_t line_end = src.find(L'\n', last_char_in_range); - if (line_end == wcstring::npos) - { + if (line_end == wcstring::npos) { line_end = src.size(); } assert(line_end >= line_start); assert(source_start >= line_start); - // Don't include the caret and line if we're interactive this is the first line, because then it's obvious + // Don't include the caret and line if we're interactive this is the first line, because + // then it's obvious. bool skip_caret = (is_interactive && source_start == 0); - if (! skip_caret) - { + if (!skip_caret) { // Append the line of text. - if (! result.empty()) - { + if (!result.empty()) { result.push_back(L'\n'); } result.append(prefix); result.append(src, line_start, line_end - line_start); - // Append the caret line. The input source may include tabs; for that reason we construct a "caret line" that has tabs in corresponding positions - const wcstring line_to_measure = prefix + wcstring(src, line_start, source_start - line_start); + // Append the caret line. The input source may include tabs; for that reason we + // construct a "caret line" that has tabs in corresponding positions. + const wcstring line_to_measure = + prefix + wcstring(src, line_start, source_start - line_start); wcstring caret_space_line; caret_space_line.reserve(source_start - line_start); - for (size_t i=0; i < line_to_measure.size(); i++) - { + for (size_t i = 0; i < line_to_measure.size(); i++) { wchar_t wc = line_to_measure.at(i); - if (wc == L'\t') - { + if (wc == L'\t') { caret_space_line.push_back(L'\t'); - } - else if (wc == L'\n') - { - /* It's possible that the source_start points at a newline itself. In that case, pretend it's a space. We only expect this to be at the end of the string. */ + } else if (wc == L'\n') { + // It's possible that the source_start points at a newline itself. In that case, + // pretend it's a space. We only expect this to be at the end of the string. caret_space_line.push_back(L' '); - } - else - { + } else { int width = fish_wcwidth(wc); - if (width > 0) - { + if (width > 0) { caret_space_line.append(static_cast(width), L' '); } } @@ -143,32 +138,26 @@ wcstring parse_error_t::describe_with_prefix(const wcstring &src, const wcstring return result; } -wcstring parse_error_t::describe(const wcstring &src) const -{ +wcstring parse_error_t::describe(const wcstring &src) const { return this->describe_with_prefix(src, wcstring(), get_is_interactive(), false); } -void parse_error_offset_source_start(parse_error_list_t *errors, size_t amt) -{ +void parse_error_offset_source_start(parse_error_list_t *errors, size_t amt) { assert(errors != NULL); - if (amt > 0) - { + if (amt > 0) { size_t i, max = errors->size(); - for (i=0; i < max; i++) - { + for (i = 0; i < max; i++) { parse_error_t *error = &errors->at(i); - /* preserve the special meaning of -1 as 'unknown' */ - if (error->source_start != SOURCE_LOCATION_UNKNOWN) - { + // Preserve the special meaning of -1 as 'unknown'. + if (error->source_start != SOURCE_LOCATION_UNKNOWN) { error->source_start += amt; } } } } -// Returns a string description for the given token type. -const wchar_t *token_type_description(parse_token_type_t type) -{ +/// Returns a string description for the given token type. +const wchar_t *token_type_description(parse_token_type_t type) { if (type >= 0 && type <= LAST_TOKEN_TYPE) return token_type_map[type]; // This leaks memory but it should never be run unless we have a bug elsewhere in the code. @@ -178,16 +167,16 @@ const wchar_t *token_type_description(parse_token_type_t type) return std::wcscpy(d2, d.c_str()); } -#define LONGIFY(x) L ## x -#define KEYWORD_MAP(x) { parse_keyword_ ## x , LONGIFY(#x) } -static const struct -{ +#define LONGIFY(x) L##x +#define KEYWORD_MAP(x) \ + { parse_keyword_##x, LONGIFY(#x) } +static const struct { const parse_keyword_t keyword; - const wchar_t * const name; + const wchar_t *const name; } keyword_map[] = { - /* Note that these must be sorted (except for the first), so that we can do binary search */ + // Note that these must be sorted (except for the first), so that we can do binary search. KEYWORD_MAP(none), KEYWORD_MAP(and), KEYWORD_MAP(begin), @@ -207,8 +196,7 @@ keyword_map[] = KEYWORD_MAP(while) }; -const wchar_t *keyword_description(parse_keyword_t type) -{ +const wchar_t *keyword_description(parse_keyword_t type) { if (type >= 0 && type <= LAST_KEYWORD) return keyword_map[type].name; // This leaks memory but it should never be run unless we have a bug elsewhere in the code. @@ -218,153 +206,144 @@ const wchar_t *keyword_description(parse_keyword_t type) return std::wcscpy(d2, d.c_str()); } -static wcstring token_type_user_presentable_description(parse_token_type_t type, parse_keyword_t keyword = parse_keyword_none) -{ - if (keyword != parse_keyword_none) - { +static wcstring token_type_user_presentable_description( + parse_token_type_t type, parse_keyword_t keyword = parse_keyword_none) { + if (keyword != parse_keyword_none) { return format_string(L"keyword '%ls'", keyword_description(keyword)); } - switch (type) - { - /* Hackish. We only support the following types. */ - case symbol_statement: + switch (type) { + // Hackish. We only support the following types. + case symbol_statement: { return L"a command"; - - case symbol_argument: + } + case symbol_argument: { return L"an argument"; - - case parse_token_type_string: + } + case parse_token_type_string: { return L"a string"; - - case parse_token_type_pipe: + } + case parse_token_type_pipe: { return L"a pipe"; - - case parse_token_type_redirection: + } + case parse_token_type_redirection: { return L"a redirection"; - - case parse_token_type_background: + } + case parse_token_type_background: { return L"a '&'"; - - case parse_token_type_end: + } + case parse_token_type_end: { return L"end of the statement"; - - case parse_token_type_terminate: + } + case parse_token_type_terminate: { return L"end of the input"; - - default: - return format_string(L"a %ls", token_type_description(type)); + } + default: { return format_string(L"a %ls", token_type_description(type)); } } } -static wcstring block_type_user_presentable_description(parse_token_type_t type) -{ - switch (type) - { - case symbol_for_header: +static wcstring block_type_user_presentable_description(parse_token_type_t type) { + switch (type) { + case symbol_for_header: { return L"for loop"; - - case symbol_while_header: + } + case symbol_while_header: { return L"while loop"; - - case symbol_function_header: + } + case symbol_function_header: { return L"function definition"; - - case symbol_begin_header: + } + case symbol_begin_header: { return L"begin"; - - case symbol_if_statement: + } + case symbol_if_statement: { return L"if statement"; - - case symbol_switch_statement: + } + case symbol_switch_statement: { return L"switch statement"; - - default: - return token_type_description(type); + } + default: { return token_type_description(type); } } } -/** Returns a string description of the given parse node */ -wcstring parse_node_t::describe() const -{ +/// Returns a string description of the given parse node. +wcstring parse_node_t::describe() const { wcstring result = token_type_description(this->type); return result; } - -/** Returns a string description of the given parse token */ -wcstring parse_token_t::describe() const -{ +/// Returns a string description of the given parse token. +wcstring parse_token_t::describe() const { wcstring result = token_type_description(type); - if (keyword != parse_keyword_none) - { + if (keyword != parse_keyword_none) { append_format(result, L" <%ls>", keyword_description(keyword)); } return result; } -/** A string description appropriate for presentation to the user */ -wcstring parse_token_t::user_presentable_description() const -{ +/// A string description appropriate for presentation to the user. +wcstring parse_token_t::user_presentable_description() const { return token_type_user_presentable_description(type, keyword); } -/* Convert from tokenizer_t's token type to a parse_token_t type */ -static inline parse_token_type_t parse_token_type_from_tokenizer_token(enum token_type tokenizer_token_type) -{ +/// Convert from tokenizer_t's token type to a parse_token_t type. +static inline parse_token_type_t parse_token_type_from_tokenizer_token( + enum token_type tokenizer_token_type) { parse_token_type_t result = token_type_invalid; - switch (tokenizer_token_type) - { - case TOK_STRING: + switch (tokenizer_token_type) { + case TOK_STRING: { result = parse_token_type_string; break; - - case TOK_PIPE: + } + case TOK_PIPE: { result = parse_token_type_pipe; break; - - case TOK_END: + } + case TOK_END: { result = parse_token_type_end; break; - - case TOK_BACKGROUND: + } + case TOK_BACKGROUND: { result = parse_token_type_background; break; - + } case TOK_REDIRECT_OUT: case TOK_REDIRECT_APPEND: case TOK_REDIRECT_IN: case TOK_REDIRECT_FD: - case TOK_REDIRECT_NOCLOB: + case TOK_REDIRECT_NOCLOB: { result = parse_token_type_redirection; break; - - case TOK_ERROR: + } + case TOK_ERROR: { result = parse_special_type_tokenizer_error; break; - - case TOK_COMMENT: + } + case TOK_COMMENT: { result = parse_special_type_comment; break; - - - default: - fprintf(stderr, "Bad token type %d passed to %s\n", (int)tokenizer_token_type, __FUNCTION__); + } + default: { + fprintf(stderr, "Bad token type %d passed to %s\n", (int)tokenizer_token_type, + __FUNCTION__); assert(0); break; + } } return result; } -/* Helper function for dump_tree */ -static void dump_tree_recursive(const parse_node_tree_t &nodes, const wcstring &src, node_offset_t node_idx, size_t indent, wcstring *result, size_t *line, node_offset_t *inout_first_node_not_dumped) -{ +/// Helper function for dump_tree. +static void dump_tree_recursive(const parse_node_tree_t &nodes, const wcstring &src, + node_offset_t node_idx, size_t indent, wcstring *result, + size_t *line, node_offset_t *inout_first_node_not_dumped) { assert(node_idx < nodes.size()); - // Update first_node_not_dumped - // This takes a bit of explanation. While it's true that a parse tree may be a "forest", its individual trees are "compact," meaning they are not interleaved. Thus we keep track of the largest node index as we descend a tree. One past the largest is the start of the next tree. - if (*inout_first_node_not_dumped <= node_idx) - { + // Update first_node_not_dumped. This takes a bit of explanation. While it's true that a parse + // tree may be a "forest", its individual trees are "compact," meaning they are not + // interleaved. Thus we keep track of the largest node index as we descend a tree. One past the + // largest is the start of the next tree. + if (*inout_first_node_not_dumped <= node_idx) { *inout_first_node_not_dumped = node_idx + 1; } @@ -372,158 +351,141 @@ static void dump_tree_recursive(const parse_node_tree_t &nodes, const wcstring & const size_t spacesPerIndent = 2; - // unindent statement lists by 1 to flatten them - if (node.type == symbol_job_list || node.type == symbol_arguments_or_redirections_list) - { + // Unindent statement lists by 1 to flatten them. + if (node.type == symbol_job_list || node.type == symbol_arguments_or_redirections_list) { if (indent > 0) indent -= 1; } append_format(*result, L"%2lu - %l2u ", *line, node_idx); - result->append(indent * spacesPerIndent, L' ');; + result->append(indent * spacesPerIndent, L' '); + ; result->append(node.describe()); - if (node.child_count > 0) - { + if (node.child_count > 0) { append_format(*result, L" <%lu children>", node.child_count); } - if (node.has_comments()) - { + if (node.has_comments()) { append_format(*result, L" ", node.child_count); } - if (node.has_source() && node.type == parse_token_type_string) - { + if (node.has_source() && node.type == parse_token_type_string) { result->append(L": \""); result->append(src, node.source_start, node.source_length); result->append(L"\""); } - if (node.type != parse_token_type_string) - { - if (node.has_source()) - { - append_format(*result, L" [%ld, %ld]", (long)node.source_start, (long)node.source_length); - } - else - { - append_format(*result, L" [no src]", (long)node.source_start, (long)node.source_length); + if (node.type != parse_token_type_string) { + if (node.has_source()) { + append_format(*result, L" [%ld, %ld]", (long)node.source_start, + (long)node.source_length); + } else { + append_format(*result, L" [no src]", (long)node.source_start, + (long)node.source_length); } } result->push_back(L'\n'); ++*line; - for (node_offset_t child_idx = node.child_start; child_idx < node.child_start + node.child_count; child_idx++) - { - dump_tree_recursive(nodes, src, child_idx, indent + 1, result, line, inout_first_node_not_dumped); + for (node_offset_t child_idx = node.child_start; + child_idx < node.child_start + node.child_count; child_idx++) { + dump_tree_recursive(nodes, src, child_idx, indent + 1, result, line, + inout_first_node_not_dumped); } } -/* Gives a debugging textual description of a parse tree. Note that this supports "parse forests" too. That is, our tree may not really be a tree, but instead a collection of trees. */ -wcstring parse_dump_tree(const parse_node_tree_t &nodes, const wcstring &src) -{ - if (nodes.empty()) - return L"(empty!)"; +/// Gives a debugging textual description of a parse tree. Note that this supports "parse forests" +/// too. That is, our tree may not really be a tree, but instead a collection of trees. +wcstring parse_dump_tree(const parse_node_tree_t &nodes, const wcstring &src) { + if (nodes.empty()) return L"(empty!)"; node_offset_t first_node_not_dumped = 0; size_t line = 0; wcstring result; - while (first_node_not_dumped < nodes.size()) - { - if (first_node_not_dumped > 0) - { + while (first_node_not_dumped < nodes.size()) { + if (first_node_not_dumped > 0) { result.append(L"---New Tree---\n"); } - dump_tree_recursive(nodes, src, first_node_not_dumped, 0, &result, &line, &first_node_not_dumped); + dump_tree_recursive(nodes, src, first_node_not_dumped, 0, &result, &line, + &first_node_not_dumped); } return result; } -/* Struct representing elements of the symbol stack, used in the internal state of the LL parser */ -struct parse_stack_element_t -{ +/// Struct representing elements of the symbol stack, used in the internal state of the LL parser. +struct parse_stack_element_t { enum parse_token_type_t type; enum parse_keyword_t keyword; node_offset_t node_idx; - explicit parse_stack_element_t(parse_token_type_t t, node_offset_t idx) : type(t), keyword(parse_keyword_none), node_idx(idx) - { - } + explicit parse_stack_element_t(parse_token_type_t t, node_offset_t idx) + : type(t), keyword(parse_keyword_none), node_idx(idx) {} - explicit parse_stack_element_t(production_element_t e, node_offset_t idx) : type(production_element_type(e)), keyword(production_element_keyword(e)), node_idx(idx) - { - } + explicit parse_stack_element_t(production_element_t e, node_offset_t idx) + : type(production_element_type(e)), keyword(production_element_keyword(e)), node_idx(idx) {} - wcstring describe(void) const - { + wcstring describe(void) const { wcstring result = token_type_description(type); - if (keyword != parse_keyword_none) - { + if (keyword != parse_keyword_none) { append_format(result, L" <%ls>", keyword_description(keyword)); } return result; } - /* Returns a name that we can show to the user, e.g. "a command" */ - wcstring user_presentable_description(void) const - { + /// Returns a name that we can show to the user, e.g. "a command". + wcstring user_presentable_description(void) const { return token_type_user_presentable_description(type, keyword); } }; -/* The parser itself, private implementation of class parse_t. This is a hand-coded table-driven LL parser. Most hand-coded LL parsers are recursive descent, but recursive descent parsers are difficult to "pause", unlike table-driven parsers. */ -class parse_ll_t -{ - /* Traditional symbol stack of the LL parser */ +/// The parser itself, private implementation of class parse_t. This is a hand-coded table-driven LL +/// parser. Most hand-coded LL parsers are recursive descent, but recursive descent parsers are +/// difficult to "pause", unlike table-driven parsers. +class parse_ll_t { + // Traditional symbol stack of the LL parser. std::vector symbol_stack; - - /* Parser output. This is a parse tree, but stored in an array. */ + // Parser output. This is a parse tree, but stored in an array. parse_node_tree_t nodes; - - /* Whether we ran into a fatal error, including parse errors or tokenizer errors */ + // Whether we ran into a fatal error, including parse errors or tokenizer errors. bool fatal_errored; - - /* Whether we should collect error messages or not */ + // Whether we should collect error messages or not. bool should_generate_error_messages; - - /* List of errors we have encountered */ + // List of errors we have encountered. parse_error_list_t errors; - - /* The symbol stack can contain terminal types or symbols. Symbols go on to do productions, but terminal types are just matched against input tokens. */ + // The symbol stack can contain terminal types or symbols. Symbols go on to do productions, but + // terminal types are just matched against input tokens. bool top_node_handle_terminal_types(parse_token_t token); void parse_error_unexpected_token(const wchar_t *expected, parse_token_t token); void parse_error(parse_token_t token, parse_error_code_t code, const wchar_t *format, ...); - void parse_error_at_location(size_t location, parse_error_code_t code, const wchar_t *format, ...); + void parse_error_at_location(size_t location, parse_error_code_t code, const wchar_t *format, + ...); void parse_error_failed_production(struct parse_stack_element_t &elem, parse_token_t token); void parse_error_unbalancing_token(parse_token_t token); - /* Reports an error for an unclosed block, e.g. 'begin;'. Returns true on success, false on failure (e.g. it is not an unclosed block) */ + // Reports an error for an unclosed block, e.g. 'begin;'. Returns true on success, false on + // failure (e.g. it is not an unclosed block). bool report_error_for_unclosed_block(); void dump_stack(void) const; - // Get the node corresponding to the top element of the stack - parse_node_t &node_for_top_symbol() - { - PARSE_ASSERT(! symbol_stack.empty()); + /// Get the node corresponding to the top element of the stack. + parse_node_t &node_for_top_symbol() { + PARSE_ASSERT(!symbol_stack.empty()); const parse_stack_element_t &top_symbol = symbol_stack.back(); PARSE_ASSERT(top_symbol.node_idx != NODE_OFFSET_INVALID); PARSE_ASSERT(top_symbol.node_idx < nodes.size()); return nodes.at(top_symbol.node_idx); } - // Pop from the top of the symbol stack, then push the given production, updating node counts. Note that production_t has type "pointer to array" so some care is required. - inline void symbol_stack_pop_push_production(const production_t *production) - { + /// Pop from the top of the symbol stack, then push the given production, updating node counts. + /// Note that production_t has type "pointer to array" so some care is required. + inline void symbol_stack_pop_push_production(const production_t *production) { bool logit = false; - if (logit) - { + if (logit) { size_t count = 0; fprintf(stderr, "Applying production:\n"); - for (size_t i=0; i < MAX_SYMBOLS_PER_PRODUCTION; i++) - { + for (size_t i = 0; i < MAX_SYMBOLS_PER_PRODUCTION; i++) { production_element_t elem = (*production)[i]; - if (production_element_is_valid(elem)) - { + if (production_element_is_valid(elem)) { parse_token_type_t type = production_element_type(elem); parse_keyword_t keyword = production_element_keyword(elem); fprintf(stderr, "\t%ls <%ls>\n", token_type_description(type), @@ -531,29 +493,29 @@ class parse_ll_t count++; } } - if (! count) fprintf(stderr, "\t\n"); + if (!count) fprintf(stderr, "\t\n"); } - // Get the parent index. But we can't get the parent parse node yet, since it may be made invalid by adding children + // Get the parent index. But we can't get the parent parse node yet, since it may be made + // invalid by adding children. const node_offset_t parent_node_idx = symbol_stack.back().node_idx; - // Add the children. Confusingly, we want our nodes to be in forwards order (last token last, so dumps look nice), but the symbols should be reverse order (last token first, so it's lowest on the stack) + // Add the children. Confusingly, we want our nodes to be in forwards order (last token + // last, so dumps look nice), but the symbols should be reverse order (last token first, so + // it's lowest on the stack) const size_t child_start_big = nodes.size(); assert(child_start_big < NODE_OFFSET_INVALID); node_offset_t child_start = static_cast(child_start_big); - // To avoid constructing multiple nodes, we make a single one that we modify + // To avoid constructing multiple nodes, we make a single one that we modify. parse_node_t representative_child(token_type_invalid); representative_child.parent = parent_node_idx; node_offset_t child_count = 0; - for (size_t i=0; i < MAX_SYMBOLS_PER_PRODUCTION; i++) - { + for (size_t i = 0; i < MAX_SYMBOLS_PER_PRODUCTION; i++) { production_element_t elem = (*production)[i]; - if (! production_element_is_valid(elem)) - { - // All done, bail out - break; + if (!production_element_is_valid(elem)) { + break; // all done, bail out } // Append the parse node. @@ -562,87 +524,78 @@ class parse_ll_t child_count++; } - // Update the parent + // Update the parent. parse_node_t &parent_node = nodes.at(parent_node_idx); - // Should have no children yet + // Should have no children yet. PARSE_ASSERT(parent_node.child_count == 0); - // Tell the node about its children + // Tell the node about its children. parent_node.child_start = child_start; parent_node.child_count = child_count; - // Replace the top of the stack with new stack elements corresponding to our new nodes. Note that these go in reverse order. + // Replace the top of the stack with new stack elements corresponding to our new nodes. Note + // that these go in reverse order. symbol_stack.pop_back(); symbol_stack.reserve(symbol_stack.size() + child_count); node_offset_t idx = child_count; - while (idx--) - { + while (idx--) { production_element_t elem = (*production)[idx]; PARSE_ASSERT(production_element_is_valid(elem)); symbol_stack.push_back(parse_stack_element_t(elem, child_start + idx)); } } -public: - - /* Constructor */ - explicit parse_ll_t(enum parse_token_type_t goal) : fatal_errored(false), should_generate_error_messages(true) - { + public: + // Constructor + explicit parse_ll_t(enum parse_token_type_t goal) + : fatal_errored(false), should_generate_error_messages(true) { this->symbol_stack.reserve(16); this->nodes.reserve(64); this->reset_symbols_and_nodes(goal); } - /* Input */ + // Input void accept_tokens(parse_token_t token1, parse_token_t token2); - /* Report tokenizer errors */ + /// Report tokenizer errors. void report_tokenizer_error(const tok_t &tok); - /* Indicate if we hit a fatal error */ - bool has_fatal_error(void) const - { - return this->fatal_errored; - } + /// Indicate if we hit a fatal error. + bool has_fatal_error(void) const { return this->fatal_errored; } - /* Indicate whether we want to generate error messages */ - void set_should_generate_error_messages(bool flag) - { + /// Indicate whether we want to generate error messages. + void set_should_generate_error_messages(bool flag) { this->should_generate_error_messages = flag; } - /* Clear the parse symbol stack (but not the node tree). Add a node of the given type as the goal node. This is called from the constructor */ + /// Clear the parse symbol stack (but not the node tree). Add a node of the given type as the + /// goal node. This is called from the constructor. void reset_symbols(enum parse_token_type_t goal); - /* Clear the parse symbol stack and the node tree. Add a node of the given type as the goal node. This is called from the constructor. */ + /// Clear the parse symbol stack and the node tree. Add a node of the given type as the goal + /// node. This is called from the constructor. void reset_symbols_and_nodes(enum parse_token_type_t goal); - /* Once parsing is complete, determine the ranges of intermediate nodes */ + /// Once parsing is complete, determine the ranges of intermediate nodes. void determine_node_ranges(); - /* Acquire output after parsing. This transfers directly from within self */ + /// Acquire output after parsing. This transfers directly from within self. void acquire_output(parse_node_tree_t *output, parse_error_list_t *errors); }; -void parse_ll_t::dump_stack(void) const -{ - // Walk backwards from the top, looking for parents +void parse_ll_t::dump_stack(void) const { + // Walk backwards from the top, looking for parents. wcstring_list_t lines; - if (symbol_stack.empty()) - { + if (symbol_stack.empty()) { lines.push_back(L"(empty)"); - } - else - { + } else { node_offset_t child = symbol_stack.back().node_idx; node_offset_t cursor = child; lines.push_back(nodes.at(cursor).describe()); - while (cursor--) - { + while (cursor--) { const parse_node_t &node = nodes.at(cursor); - if (node.child_start <= child && node.child_start + node.child_count > child) - { + if (node.child_start <= child && node.child_start + node.child_count > child) { lines.push_back(node.describe()); child = cursor; } @@ -650,66 +603,60 @@ void parse_ll_t::dump_stack(void) const } fprintf(stderr, "Stack dump (%zu elements):\n", symbol_stack.size()); - for (size_t idx = 0; idx < lines.size(); idx++) - { + for (size_t idx = 0; idx < lines.size(); idx++) { fprintf(stderr, " %ls\n", lines.at(idx).c_str()); } } -// Give each node a source range equal to the union of the ranges of its children -// Terminal nodes already have source ranges (and no children) -// Since children always appear after their parents, we can implement this very simply by walking backwards -// We then do a second pass to give empty nodes an empty source range (but with a valid offset) -// We do this by walking forward. If a child of a node has an invalid source range, we set it equal to the end of the source range of its previous child -void parse_ll_t::determine_node_ranges(void) -{ +// Give each node a source range equal to the union of the ranges of its children. Terminal nodes +// already have source ranges (and no children). Since children always appear after their parents, +// we can implement this very simply by walking backwards. We then do a second pass to give empty +// nodes an empty source range (but with a valid offset). We do this by walking forward. If a child +// of a node has an invalid source range, we set it equal to the end of the source range of its +// previous child. +void parse_ll_t::determine_node_ranges(void) { size_t idx = nodes.size(); - while (idx--) - { + while (idx--) { parse_node_t *parent = &nodes[idx]; // Skip nodes that already have a source range. These are terminal nodes. - if (parent->source_start != SOURCE_OFFSET_INVALID) - continue; + if (parent->source_start != SOURCE_OFFSET_INVALID) continue; // Ok, this node needs a source range. Get all of its children, and then set its range. - source_offset_t min_start = SOURCE_OFFSET_INVALID, max_end = 0; //note SOURCE_OFFSET_INVALID is huge - for (node_offset_t i=0; i < parent->child_count; i++) - { + source_offset_t min_start = SOURCE_OFFSET_INVALID, + max_end = 0; // note SOURCE_OFFSET_INVALID is huge + for (node_offset_t i = 0; i < parent->child_count; i++) { const parse_node_t &child = nodes.at(parent->child_offset(i)); - if (child.has_source()) - { + if (child.has_source()) { min_start = std::min(min_start, child.source_start); max_end = std::max(max_end, child.source_start + child.source_length); } } - if (min_start != SOURCE_OFFSET_INVALID) - { + if (min_start != SOURCE_OFFSET_INVALID) { assert(max_end >= min_start); parent->source_start = min_start; parent->source_length = max_end - min_start; } } - /* Forwards pass */ + // Forward pass. size_t size = nodes.size(); - for (idx = 0; idx < size; idx++) - { - /* Since we populate the source range based on the sibling node, it's simpler to walk over the children of each node. - We keep a running "child_source_cursor" which is meant to be the end of the child's source range. It's initially set to the beginning of the parent' source range. */ + for (idx = 0; idx < size; idx++) { + // Since we populate the source range based on the sibling node, it's simpler to walk over + // the children of each node. We keep a running "child_source_cursor" which is meant to be + // the end of the child's source range. It's initially set to the beginning of the parent' + // source range. parse_node_t *parent = &nodes[idx]; - // If the parent doesn't have a valid source range, then none of its children will either; skip it entirely - if (parent->source_start == SOURCE_OFFSET_INVALID) - { + // If the parent doesn't have a valid source range, then none of its children will either; + // skip it entirely. + if (parent->source_start == SOURCE_OFFSET_INVALID) { continue; } source_offset_t child_source_cursor = parent->source_start; - for (size_t child_idx = 0; child_idx < parent->child_count; child_idx++) - { + for (size_t child_idx = 0; child_idx < parent->child_count; child_idx++) { parse_node_t *child = &nodes[parent->child_start + child_idx]; - if (child->source_start == SOURCE_OFFSET_INVALID) - { + if (child->source_start == SOURCE_OFFSET_INVALID) { child->source_start = child_source_cursor; } child_source_cursor = child->source_start + child->source_length; @@ -717,28 +664,24 @@ void parse_ll_t::determine_node_ranges(void) } } -void parse_ll_t::acquire_output(parse_node_tree_t *output, parse_error_list_t *errors) -{ - if (output != NULL) - { +void parse_ll_t::acquire_output(parse_node_tree_t *output, parse_error_list_t *errors) { + if (output != NULL) { output->swap(this->nodes); } this->nodes.clear(); - if (errors != NULL) - { + if (errors != NULL) { errors->swap(this->errors); } this->errors.clear(); this->symbol_stack.clear(); } -void parse_ll_t::parse_error(parse_token_t token, parse_error_code_t code, const wchar_t *fmt, ...) -{ +void parse_ll_t::parse_error(parse_token_t token, parse_error_code_t code, const wchar_t *fmt, + ...) { this->fatal_errored = true; - if (this->should_generate_error_messages) - { - //this->dump_stack(); + if (this->should_generate_error_messages) { + // this->dump_stack(); parse_error_t err; va_list va; @@ -753,12 +696,11 @@ void parse_ll_t::parse_error(parse_token_t token, parse_error_code_t code, const } } -void parse_ll_t::parse_error_at_location(size_t source_location, parse_error_code_t code, const wchar_t *fmt, ...) -{ +void parse_ll_t::parse_error_at_location(size_t source_location, parse_error_code_t code, + const wchar_t *fmt, ...) { this->fatal_errored = true; - if (this->should_generate_error_messages) - { - //this->dump_stack(); + if (this->should_generate_error_messages) { + // this->dump_stack(); parse_error_t err; va_list va; @@ -774,226 +716,222 @@ void parse_ll_t::parse_error_at_location(size_t source_location, parse_error_cod } // Unbalancing token. This includes 'else' or 'case' or 'end' outside of the appropriate block -// This essentially duplicates some logic from resolving the production for symbol_statement_list - yuck -void parse_ll_t::parse_error_unbalancing_token(parse_token_t token) -{ +// This essentially duplicates some logic from resolving the production for symbol_statement_list - +// yuck. +void parse_ll_t::parse_error_unbalancing_token(parse_token_t token) { this->fatal_errored = true; - if (this->should_generate_error_messages) - { - switch (token.keyword) - { - case parse_keyword_end: + if (this->should_generate_error_messages) { + switch (token.keyword) { + case parse_keyword_end: { this->parse_error(token, parse_error_unbalancing_end, L"'end' outside of a block"); break; - - case parse_keyword_else: - this->parse_error(token, parse_error_unbalancing_else, L"'else' builtin not inside of if block"); + } + case parse_keyword_else: { + this->parse_error(token, parse_error_unbalancing_else, + L"'else' builtin not inside of if block"); break; - - case parse_keyword_case: - this->parse_error(token, parse_error_unbalancing_case, L"'case' builtin not inside of switch block"); + } + case parse_keyword_case: { + this->parse_error(token, parse_error_unbalancing_case, + L"'case' builtin not inside of switch block"); break; - - default: - // At the moment, this case should only be hit if you parse a freestanding_argument_list - // For example, 'complete -c foo -a 'one & three' - // Hackish error message for that case - if (! symbol_stack.empty() && symbol_stack.back().type == symbol_freestanding_argument_list) - { - this->parse_error(token, parse_error_generic, L"Expected %ls, but found %ls", token_type_user_presentable_description(symbol_argument).c_str(), token.user_presentable_description().c_str()); - } - else - { - this->parse_error(token, parse_error_generic, L"Did not expect %ls", token.user_presentable_description().c_str()); + } + default: { + // At the moment, this case should only be hit if you parse a + // freestanding_argument_list. For example, 'complete -c foo -a 'one & three'. + // Hackish error message for that case. + if (!symbol_stack.empty() && + symbol_stack.back().type == symbol_freestanding_argument_list) { + this->parse_error( + token, parse_error_generic, L"Expected %ls, but found %ls", + token_type_user_presentable_description(symbol_argument).c_str(), + token.user_presentable_description().c_str()); + } else { + this->parse_error(token, parse_error_generic, L"Did not expect %ls", + token.user_presentable_description().c_str()); } break; + } } } } -// This is a 'generic' parse error when we can't match the top of the stack element -void parse_ll_t::parse_error_failed_production(struct parse_stack_element_t &stack_elem, parse_token_t token) -{ +/// This is a 'generic' parse error when we can't match the top of the stack element. +void parse_ll_t::parse_error_failed_production(struct parse_stack_element_t &stack_elem, + parse_token_t token) { fatal_errored = true; - if (this->should_generate_error_messages) - { + if (this->should_generate_error_messages) { bool done = false; - /* Check for || */ - if (token.type == parse_token_type_pipe && token.source_start > 0) - { - /* Here we wanted a statement and instead got a pipe. See if this is a double pipe: foo || bar. If so, we have a special error message. */ - const parse_node_t *prev_pipe = nodes.find_node_matching_source_location(parse_token_type_pipe, token.source_start - 1, NULL); - if (prev_pipe != NULL) - { - /* The pipe of the previous job abuts our current token. So we have ||. */ + // Check for ||. + if (token.type == parse_token_type_pipe && token.source_start > 0) { + // Here we wanted a statement and instead got a pipe. See if this is a double pipe: foo + // || bar. If so, we have a special error message. + const parse_node_t *prev_pipe = nodes.find_node_matching_source_location( + parse_token_type_pipe, token.source_start - 1, NULL); + if (prev_pipe != NULL) { + // The pipe of the previous job abuts our current token. So we have ||. this->parse_error(token, parse_error_double_pipe, ERROR_BAD_OR); done = true; } } - /* Check for && */ - if (! done && token.type == parse_token_type_background && token.source_start > 0) - { - /* Check to see if there was a previous token_background */ - const parse_node_t *prev_background = nodes.find_node_matching_source_location(parse_token_type_background, token.source_start - 1, NULL); - if (prev_background != NULL) - { - /* We have &&. */ + // Check for &&. + if (!done && token.type == parse_token_type_background && token.source_start > 0) { + // Check to see if there was a previous token_background. + const parse_node_t *prev_background = nodes.find_node_matching_source_location( + parse_token_type_background, token.source_start - 1, NULL); + if (prev_background != NULL) { + // We have &&. this->parse_error(token, parse_error_double_background, ERROR_BAD_AND); done = true; } } - if (! done) - { + if (!done) { const wcstring expected = stack_elem.user_presentable_description(); this->parse_error_unexpected_token(expected.c_str(), token); } } } -void parse_ll_t::report_tokenizer_error(const tok_t &tok) -{ +void parse_ll_t::report_tokenizer_error(const tok_t &tok) { parse_error_code_t parse_error_code; - switch (tok.error) - { - case TOK_UNTERMINATED_QUOTE: + switch (tok.error) { + case TOK_UNTERMINATED_QUOTE: { parse_error_code = parse_error_tokenizer_unterminated_quote; break; - - case TOK_UNTERMINATED_SUBSHELL: + } + case TOK_UNTERMINATED_SUBSHELL: { parse_error_code = parse_error_tokenizer_unterminated_subshell; break; - - case TOK_UNTERMINATED_SLICE: + } + case TOK_UNTERMINATED_SLICE: { parse_error_code = parse_error_tokenizer_unterminated_slice; break; - - case TOK_UNTERMINATED_ESCAPE: + } + case TOK_UNTERMINATED_ESCAPE: { parse_error_code = parse_error_tokenizer_unterminated_escape; break; - + } case TOK_OTHER: - default: + default: { parse_error_code = parse_error_tokenizer_other; break; - + } } - this->parse_error_at_location(tok.offset + tok.error_offset, parse_error_code, L"%ls", tok.text.c_str()); + this->parse_error_at_location(tok.offset + tok.error_offset, parse_error_code, L"%ls", + tok.text.c_str()); } -void parse_ll_t::parse_error_unexpected_token(const wchar_t *expected, parse_token_t token) -{ +void parse_ll_t::parse_error_unexpected_token(const wchar_t *expected, parse_token_t token) { fatal_errored = true; - if (this->should_generate_error_messages) - { - this->parse_error(token, parse_error_generic, L"Expected %ls, but instead found %ls", expected, token.user_presentable_description().c_str()); + if (this->should_generate_error_messages) { + this->parse_error(token, parse_error_generic, L"Expected %ls, but instead found %ls", + expected, token.user_presentable_description().c_str()); } } -void parse_ll_t::reset_symbols(enum parse_token_type_t goal) -{ - /* Add a new goal node, and then reset our symbol list to point at it */ +void parse_ll_t::reset_symbols(enum parse_token_type_t goal) { + // Add a new goal node, and then reset our symbol list to point at it. node_offset_t where = static_cast(nodes.size()); nodes.push_back(parse_node_t(goal)); symbol_stack.clear(); - symbol_stack.push_back(parse_stack_element_t(goal, where)); // goal token + symbol_stack.push_back(parse_stack_element_t(goal, where)); // goal token this->fatal_errored = false; } -/* Reset both symbols and nodes */ -void parse_ll_t::reset_symbols_and_nodes(enum parse_token_type_t goal) -{ +/// Reset both symbols and nodes. +void parse_ll_t::reset_symbols_and_nodes(enum parse_token_type_t goal) { nodes.clear(); this->reset_symbols(goal); } -static bool type_is_terminal_type(parse_token_type_t type) -{ - switch (type) - { +static bool type_is_terminal_type(parse_token_type_t type) { + switch (type) { case parse_token_type_string: case parse_token_type_pipe: case parse_token_type_redirection: case parse_token_type_background: case parse_token_type_end: - case parse_token_type_terminate: + case parse_token_type_terminate: { return true; - - default: - return false; + } + default: { return false; } } } -bool parse_ll_t::report_error_for_unclosed_block() -{ +bool parse_ll_t::report_error_for_unclosed_block() { bool reported_error = false; - /* Unclosed block, for example, 'while true ; '. We want to show the block node that opened it. */ + // Unclosed block, for example, 'while true ; '. We want to show the block node that opened it. const parse_node_t &top_node = this->node_for_top_symbol(); - /* Hacktastic. We want to point at the source location of the block, but our block doesn't have a source range yet - only the terminal tokens do. So get the block statement corresponding to this end command. In general this block may be of a variety of types: if_statement, switch_statement, etc., each with different node structures. But keep descending the first child and eventually you hit a keyword: begin, if, etc. That's the keyword we care about. */ + // Hacktastic. We want to point at the source location of the block, but our block doesn't have + // a source range yet - only the terminal tokens do. So get the block statement corresponding to + // this end command. In general this block may be of a variety of types: if_statement, + // switch_statement, etc., each with different node structures. But keep descending the first + // child and eventually you hit a keyword: begin, if, etc. That's the keyword we care about. const parse_node_t *end_command = this->nodes.get_parent(top_node, symbol_end_command); const parse_node_t *block_node = end_command ? this->nodes.get_parent(*end_command) : NULL; - if (block_node && block_node->type == symbol_block_statement) - { - // Get the header + if (block_node && block_node->type == symbol_block_statement) { + // Get the header. block_node = this->nodes.get_child(*block_node, 0, symbol_block_header); - block_node = this->nodes.get_child(*block_node, 0); // specific statement + block_node = this->nodes.get_child(*block_node, 0); // specific statement } - if (block_node != NULL) - { - // block_node is now an if_statement, switch_statement, for_header, while_header, function_header, or begin_header - // Hackish: descend down the first node until we reach the bottom. This will be a keyword node like SWITCH, which will have the source range. Ordinarily the source range would be known by the parent node too, but we haven't completed parsing yet, so we haven't yet propagated source ranges + if (block_node != NULL) { + // block_node is now an if_statement, switch_statement, for_header, while_header, + // function_header, or begin_header. + // + // Hackish: descend down the first node until we reach the bottom. This will be a keyword + // node like SWITCH, which will have the source range. Ordinarily the source range would be + // known by the parent node too, but we haven't completed parsing yet, so we haven't yet + // propagated source ranges. const parse_node_t *cursor = block_node; - while (cursor->child_count > 0) - { + while (cursor->child_count > 0) { cursor = this->nodes.get_child(*cursor, 0); assert(cursor != NULL); } - if (cursor->source_start != NODE_OFFSET_INVALID) - { + if (cursor->source_start != NODE_OFFSET_INVALID) { const wcstring node_desc = block_type_user_presentable_description(block_node->type); - this->parse_error_at_location(cursor->source_start, parse_error_generic, L"Missing end to balance this %ls", node_desc.c_str()); + this->parse_error_at_location(cursor->source_start, parse_error_generic, + L"Missing end to balance this %ls", node_desc.c_str()); reported_error = true; } } return reported_error; } -bool parse_ll_t::top_node_handle_terminal_types(parse_token_t token) -{ - PARSE_ASSERT(! symbol_stack.empty()); +bool parse_ll_t::top_node_handle_terminal_types(parse_token_t token) { + PARSE_ASSERT(!symbol_stack.empty()); PARSE_ASSERT(token.type >= FIRST_PARSE_TOKEN_TYPE); bool handled = false; parse_stack_element_t &stack_top = symbol_stack.back(); - if (type_is_terminal_type(stack_top.type)) - { - // The top of the stack is terminal. We are going to handle this (because we can't produce from a terminal type) + if (type_is_terminal_type(stack_top.type)) { + // The top of the stack is terminal. We are going to handle this (because we can't produce + // from a terminal type). handled = true; // Now see if we actually matched bool matched = false; - if (stack_top.type == token.type) - { - switch (stack_top.type) - { - case parse_token_type_string: - // We matched if the keywords match, or no keyword was required - matched = (stack_top.keyword == parse_keyword_none || stack_top.keyword == token.keyword); + if (stack_top.type == token.type) { + switch (stack_top.type) { + case parse_token_type_string: { + // We matched if the keywords match, or no keyword was required. + matched = (stack_top.keyword == parse_keyword_none || + stack_top.keyword == token.keyword); break; - - default: - // For other types, we only require that the types match + } + default: { + // For other types, we only require that the types match. matched = true; break; + } } } - if (matched) - { + if (matched) { // Success. Tell the node that it matched this token, and what its source range is in // the parse phase, we only set source ranges for terminal types. We propagate ranges to // parent nodes afterwards. @@ -1001,76 +939,78 @@ bool parse_ll_t::top_node_handle_terminal_types(parse_token_t token) node.keyword = token.keyword; node.source_start = token.source_start; node.source_length = token.source_length; - } - else - { + } else { // Failure - if (stack_top.type == parse_token_type_string && token.type == parse_token_type_string) - { + if (stack_top.type == parse_token_type_string && + token.type == parse_token_type_string) { // Keyword failure. We should unify this with the 'matched' computation above. - assert(stack_top.keyword != parse_keyword_none && stack_top.keyword != token.keyword); + assert(stack_top.keyword != parse_keyword_none && + stack_top.keyword != token.keyword); - // Check to see which keyword we got which was considered wrong - switch (token.keyword) - { - // Some keywords are only valid in certain contexts. If this cascaded all the way down through the outermost job_list, it was not in a valid context. + // Check to see which keyword we got which was considered wrong. + switch (token.keyword) { + // Some keywords are only valid in certain contexts. If this cascaded all the + // way down through the outermost job_list, it was not in a valid context. case parse_keyword_case: case parse_keyword_end: - case parse_keyword_else: + case parse_keyword_else: { this->parse_error_unbalancing_token(token); break; - - case parse_keyword_none: - { - // This is a random other string (not a keyword) + } + case parse_keyword_none: { + // This is a random other string (not a keyword). const wcstring expected = keyword_description(stack_top.keyword); - this->parse_error(token, parse_error_generic, L"Expected keyword '%ls'", expected.c_str()); + this->parse_error(token, parse_error_generic, L"Expected keyword '%ls'", + expected.c_str()); break; } - - - default: - { - // Got a real keyword we can report - const wcstring actual = (token.keyword == parse_keyword_none ? token.describe() : keyword_description(token.keyword)); + default: { + // Got a real keyword we can report. + const wcstring actual = (token.keyword == parse_keyword_none + ? token.describe() + : keyword_description(token.keyword)); const wcstring expected = keyword_description(stack_top.keyword); - this->parse_error(token, parse_error_generic, L"Expected keyword '%ls', instead got keyword '%ls'", expected.c_str(), actual.c_str()); + this->parse_error(token, parse_error_generic, + L"Expected keyword '%ls', instead got keyword '%ls'", + expected.c_str(), actual.c_str()); break; } } - } - else if (stack_top.keyword == parse_keyword_end && token.type == parse_token_type_terminate && this->report_error_for_unclosed_block()) - { - // Handled by report_error_for_unclosed_block - } - else - { + } else if (stack_top.keyword == parse_keyword_end && + token.type == parse_token_type_terminate && + this->report_error_for_unclosed_block()) { + // Handled by report_error_for_unclosed_block. + } else { const wcstring expected = stack_top.user_presentable_description(); this->parse_error_unexpected_token(expected.c_str(), token); } } - // We handled the token, so pop the symbol stack + // We handled the token, so pop the symbol stack. symbol_stack.pop_back(); } return handled; } -void parse_ll_t::accept_tokens(parse_token_t token1, parse_token_t token2) -{ +void parse_ll_t::accept_tokens(parse_token_t token1, parse_token_t token2) { bool logit = false; - if (logit) - { + if (logit) { fprintf(stderr, "Accept token %ls\n", token1.describe().c_str()); } PARSE_ASSERT(token1.type >= FIRST_PARSE_TOKEN_TYPE); bool consumed = false; - // Handle special types specially. Note that these are the only types that can be pushed if the symbol stack is empty. - if (token1.type == parse_special_type_parse_error || token1.type == parse_special_type_tokenizer_error || token1.type == parse_special_type_comment) - { - /* We set the special node's parent to the top of the stack. This means that we have an asymmetric relationship: the special node has a parent (which is the node we were trying to generate when we encountered the special node), but the parent node does not have the special node as a child. This means for example that parents don't have to worry about tracking any comment nodes, but we can still recover the parent from the comment. */ + // Handle special types specially. Note that these are the only types that can be pushed if the + // symbol stack is empty. + if (token1.type == parse_special_type_parse_error || + token1.type == parse_special_type_tokenizer_error || + token1.type == parse_special_type_comment) { + // We set the special node's parent to the top of the stack. This means that we have an + // asymmetric relationship: the special node has a parent (which is the node we were trying + // to generate when we encountered the special node), but the parent node does not have the + // special node as a child. This means for example that parents don't have to worry about + // tracking any comment nodes, but we can still recover the parent from the comment. parse_node_t special_node(token1.type); special_node.parent = symbol_stack.back().node_idx; special_node.source_start = token1.source_start; @@ -1078,143 +1018,124 @@ void parse_ll_t::accept_tokens(parse_token_t token1, parse_token_t token2) nodes.push_back(special_node); consumed = true; - /* Mark special flags */ - if (token1.type == parse_special_type_comment) - { + // Mark special flags. + if (token1.type == parse_special_type_comment) { this->node_for_top_symbol().flags |= parse_node_flag_has_comments; } - /* tokenizer errors are fatal */ - if (token1.type == parse_special_type_tokenizer_error) - this->fatal_errored = true; + // Tokenizer errors are fatal. + if (token1.type == parse_special_type_tokenizer_error) this->fatal_errored = true; } - while (! consumed && ! this->fatal_errored) - { - PARSE_ASSERT(! symbol_stack.empty()); + while (!consumed && !this->fatal_errored) { + PARSE_ASSERT(!symbol_stack.empty()); - if (top_node_handle_terminal_types(token1)) - { - if (logit) - { + if (top_node_handle_terminal_types(token1)) { + if (logit) { fprintf(stderr, "Consumed token %ls\n", token1.describe().c_str()); } consumed = true; break; } - // top_node_match_token may indicate an error if our stack is empty - if (this->fatal_errored) - break; + // top_node_match_token may indicate an error if our stack is empty. + if (this->fatal_errored) break; - // Get the production for the top of the stack + // Get the production for the top of the stack. parse_stack_element_t &stack_elem = symbol_stack.back(); parse_node_t &node = nodes.at(stack_elem.node_idx); parse_node_tag_t tag = 0; - const production_t *production = production_for_token(stack_elem.type, token1, token2, &tag); + const production_t *production = + production_for_token(stack_elem.type, token1, token2, &tag); node.tag = tag; - if (production == NULL) - { + if (production == NULL) { parse_error_failed_production(stack_elem, token1); - // the above sets fatal_errored, which ends the loop - } - else - { + // The above sets fatal_errored, which ends the loop. + } else { bool is_terminate = (token1.type == parse_token_type_terminate); - // When a job_list encounters something like 'else', it returns an empty production to return control to the outer block. But if it's unbalanced, then we'll end up with an empty stack! So make sure that doesn't happen. This is the primary mechanism by which we detect e.g. unbalanced end. However, if we get a true terminate token, then we allow (expect) this to empty the stack - if (symbol_stack.size() == 1 && production_is_empty(production) && ! is_terminate) - { + // When a job_list encounters something like 'else', it returns an empty production to + // return control to the outer block. But if it's unbalanced, then we'll end up with an + // empty stack! So make sure that doesn't happen. This is the primary mechanism by which + // we detect e.g. unbalanced end. However, if we get a true terminate token, then we + // allow (expect) this to empty the stack. + if (symbol_stack.size() == 1 && production_is_empty(production) && !is_terminate) { this->parse_error_unbalancing_token(token1); break; } - // Manipulate the symbol stack. - // Note that stack_elem is invalidated by popping the stack. + // Manipulate the symbol stack. Note that stack_elem is invalidated by popping the + // stack. symbol_stack_pop_push_production(production); - // Expect to not have an empty stack, unless this was the terminate type - // Note we may not have an empty stack with the terminate type (i.e. incomplete input) - assert(is_terminate || ! symbol_stack.empty()); + // Expect to not have an empty stack, unless this was the terminate type. Note we may + // not have an empty stack with the terminate type (i.e. incomplete input). + assert(is_terminate || !symbol_stack.empty()); - if (symbol_stack.empty()) - { + if (symbol_stack.empty()) { break; } } } } -/* Given an expanded string, returns any keyword it matches */ -static parse_keyword_t keyword_with_name(const wchar_t *name) -{ - /* Binary search on keyword_map. Start at 1 since 0 is keyword_none */ +// Given an expanded string, returns any keyword it matches. +static parse_keyword_t keyword_with_name(const wchar_t *name) { + // Binary search on keyword_map. Start at 1 since 0 is keyword_none. parse_keyword_t result = parse_keyword_none; size_t left = 1, right = sizeof keyword_map / sizeof *keyword_map; - while (left < right) - { - size_t mid = left + (right - left)/2; + while (left < right) { + size_t mid = left + (right - left) / 2; int cmp = wcscmp(name, keyword_map[mid].name); - if (cmp < 0) - { - right = mid; // name was smaller than mid - } - else if (cmp > 0) - { - left = mid + 1; // name was larger than mid - } - else - { - result = keyword_map[mid].keyword; // found it + if (cmp < 0) { + right = mid; // name was smaller than mid + } else if (cmp > 0) { + left = mid + 1; // name was larger than mid + } else { + result = keyword_map[mid].keyword; // found it break; } } return result; } -static bool is_keyword_char(wchar_t c) -{ - return (c >= L'a' && c <= L'z') || (c >= L'A' && c <= L'Z') || (c >= L'0' && c <= L'9') - || c == L'\'' || c == L'"' || c == L'\\' || c == '\n'; +static bool is_keyword_char(wchar_t c) { + return (c >= L'a' && c <= L'z') || (c >= L'A' && c <= L'Z') || (c >= L'0' && c <= L'9') || + c == L'\'' || c == L'"' || c == L'\\' || c == '\n'; } -/* Given a token, returns the keyword it matches, or parse_keyword_none. */ -static parse_keyword_t keyword_for_token(token_type tok, const wcstring &token) -{ +/// Given a token, returns the keyword it matches, or parse_keyword_none. +static parse_keyword_t keyword_for_token(token_type tok, const wcstring &token) { /* Only strings can be keywords */ - if (tok != TOK_STRING) - { + if (tok != TOK_STRING) { return parse_keyword_none; } - - /* If tok_txt is clean (which most are), we can compare it directly. Otherwise we have to expand it. We only expand quotes, and we don't want to do expensive expansions like tilde expansions. So we do our own "cleanliness" check; if we find a character not in our allowed set we know it's not a keyword, and if we never find a quote we don't have to expand! Note that this lowercase set could be shrunk to be just the characters that are in keywords. */ + + // If tok_txt is clean (which most are), we can compare it directly. Otherwise we have to expand + // it. We only expand quotes, and we don't want to do expensive expansions like tilde + // expansions. So we do our own "cleanliness" check; if we find a character not in our allowed + // set we know it's not a keyword, and if we never find a quote we don't have to expand! Note + // that this lowercase set could be shrunk to be just the characters that are in keywords. parse_keyword_t result = parse_keyword_none; bool needs_expand = false, all_chars_valid = true; const wchar_t *tok_txt = token.c_str(); - for (size_t i=0; tok_txt[i] != L'\0'; i++) - { + for (size_t i = 0; tok_txt[i] != L'\0'; i++) { wchar_t c = tok_txt[i]; - if (! is_keyword_char(c)) - { + if (!is_keyword_char(c)) { all_chars_valid = false; break; } - // If we encounter a quote, we need expansion + // If we encounter a quote, we need expansion. needs_expand = needs_expand || c == L'"' || c == L'\'' || c == L'\\'; } - - if (all_chars_valid) - { - /* Expand if necessary */ - if (! needs_expand) - { + + if (all_chars_valid) { + // Expand if necessary. + if (!needs_expand) { result = keyword_with_name(tok_txt); - } - else - { + } else { wcstring storage; - if (unescape_string(tok_txt, &storage, 0)) - { + if (unescape_string(tok_txt, &storage, 0)) { result = keyword_with_name(storage.c_str()); } } @@ -1222,123 +1143,125 @@ static parse_keyword_t keyword_for_token(token_type tok, const wcstring &token) return result; } -/* Placeholder invalid token */ -static const parse_token_t kInvalidToken = {token_type_invalid, parse_keyword_none, false, false, SOURCE_OFFSET_INVALID, 0}; +/// Placeholder invalid token. +static const parse_token_t kInvalidToken = { + token_type_invalid, parse_keyword_none, false, false, SOURCE_OFFSET_INVALID, 0}; -/* Terminal token */ -static const parse_token_t kTerminalToken = {parse_token_type_terminate, parse_keyword_none, false, false, SOURCE_OFFSET_INVALID, 0}; +/// Terminal token. +static const parse_token_t kTerminalToken = { + parse_token_type_terminate, parse_keyword_none, false, false, SOURCE_OFFSET_INVALID, 0}; -static inline bool is_help_argument(const wcstring &txt) -{ - return contains(txt, L"-h", L"--help"); -} +static inline bool is_help_argument(const wcstring &txt) { return contains(txt, L"-h", L"--help"); } -/* Return a new parse token, advancing the tokenizer */ -static inline parse_token_t next_parse_token(tokenizer_t *tok, tok_t *token) -{ - if (! tok->next(token)) - { +/// Return a new parse token, advancing the tokenizer. +static inline parse_token_t next_parse_token(tokenizer_t *tok, tok_t *token) { + if (!tok->next(token)) { return kTerminalToken; } parse_token_t result; - /* Set the type, keyword, and whether there's a dash prefix. Note that this is quite sketchy, because it ignores quotes. This is the historical behavior. For example, `builtin --names` lists builtins, but `builtin "--names"` attempts to run --names as a command. Amazingly as of this writing (10/12/13) nobody seems to have noticed this. Squint at it really hard and it even starts to look like a feature. */ + // Set the type, keyword, and whether there's a dash prefix. Note that this is quite sketchy, + // because it ignores quotes. This is the historical behavior. For example, `builtin --names` + // lists builtins, but `builtin "--names"` attempts to run --names as a command. Amazingly as of + // this writing (10/12/13) nobody seems to have noticed this. Squint at it really hard and it + // even starts to look like a feature. result.type = parse_token_type_from_tokenizer_token(token->type); result.keyword = keyword_for_token(token->type, token->text); result.has_dash_prefix = !token->text.empty() && token->text.at(0) == L'-'; result.is_help_argument = result.has_dash_prefix && is_help_argument(token->text); - - /* These assertions are totally bogus. Basically our tokenizer works in size_t but we work in uint32_t to save some space. If we have a source file larger than 4 GB, we'll probably just crash. */ + + // These assertions are totally bogus. Basically our tokenizer works in size_t but we work in + // uint32_t to save some space. If we have a source file larger than 4 GB, we'll probably just + // crash. assert(token->offset < SOURCE_OFFSET_INVALID); result.source_start = (source_offset_t)token->offset; - + assert(token->length <= SOURCE_OFFSET_INVALID); result.source_length = (source_offset_t)token->length; return result; } -bool parse_tree_from_string(const wcstring &str, parse_tree_flags_t parse_flags, parse_node_tree_t *output, parse_error_list_t *errors, parse_token_type_t goal) -{ +bool parse_tree_from_string(const wcstring &str, parse_tree_flags_t parse_flags, + parse_node_tree_t *output, parse_error_list_t *errors, + parse_token_type_t goal) { parse_ll_t parser(goal); parser.set_should_generate_error_messages(errors != NULL); - /* Construct the tokenizer */ + // Construct the tokenizer. tok_flags_t tok_options = 0; - if (parse_flags & parse_flag_include_comments) - tok_options |= TOK_SHOW_COMMENTS; + if (parse_flags & parse_flag_include_comments) tok_options |= TOK_SHOW_COMMENTS; - if (parse_flags & parse_flag_accept_incomplete_tokens) - tok_options |= TOK_ACCEPT_UNFINISHED; + if (parse_flags & parse_flag_accept_incomplete_tokens) tok_options |= TOK_ACCEPT_UNFINISHED; - if (parse_flags & parse_flag_show_blank_lines) - tok_options |= TOK_SHOW_BLANK_LINES; + if (parse_flags & parse_flag_show_blank_lines) tok_options |= TOK_SHOW_BLANK_LINES; - if (errors == NULL) - tok_options |= TOK_SQUASH_ERRORS; + if (errors == NULL) tok_options |= TOK_SQUASH_ERRORS; tokenizer_t tok(str.c_str(), tok_options); - /* We are an LL(2) parser. We pass two tokens at a time. New tokens come in at index 1. Seed our queue with an initial token at index 1. */ + // We are an LL(2) parser. We pass two tokens at a time. New tokens come in at index 1. Seed our + // queue with an initial token at index 1. parse_token_t queue[2] = {kInvalidToken, kInvalidToken}; - /* Loop until we have a terminal token. */ + // Loop until we have a terminal token. tok_t tokenizer_token; - for (size_t token_count = 0; queue[0].type != parse_token_type_terminate; token_count++) - { - /* Push a new token onto the queue */ + for (size_t token_count = 0; queue[0].type != parse_token_type_terminate; token_count++) { + // Push a new token onto the queue. queue[0] = queue[1]; queue[1] = next_parse_token(&tok, &tokenizer_token); - /* If we are leaving things unterminated, then don't pass parse_token_type_terminate */ - if (queue[0].type == parse_token_type_terminate && (parse_flags & parse_flag_leave_unterminated)) - { + // If we are leaving things unterminated, then don't pass parse_token_type_terminate. + if (queue[0].type == parse_token_type_terminate && + (parse_flags & parse_flag_leave_unterminated)) { break; } - /* Pass these two tokens, unless we're still loading the queue. We know that queue[0] is valid; queue[1] may be invalid. */ - if (token_count > 0) - { + // Pass these two tokens, unless we're still loading the queue. We know that queue[0] is + // valid; queue[1] may be invalid. + if (token_count > 0) { parser.accept_tokens(queue[0], queue[1]); } - /* Handle tokenizer errors. This is a hack because really the parser should report this for itself; but it has no way of getting the tokenizer message */ - if (queue[1].type == parse_special_type_tokenizer_error) - { + // Handle tokenizer errors. This is a hack because really the parser should report this for + // itself; but it has no way of getting the tokenizer message. + if (queue[1].type == parse_special_type_tokenizer_error) { parser.report_tokenizer_error(tokenizer_token); } - /* Handle errors */ - if (parser.has_fatal_error()) - { - if (parse_flags & parse_flag_continue_after_error) - { - /* Hack hack hack. Typically the parse error is due to the first token. However, if it's a tokenizer error, then has_fatal_error was set due to the check above; in that case the second token is what matters. */ + // Handle errors. + if (parser.has_fatal_error()) { + if (parse_flags & parse_flag_continue_after_error) { + // Hack. Typically the parse error is due to the first token. However, if it's a + // tokenizer error, then has_fatal_error was set due to the check above; in that + // case the second token is what matters. size_t error_token_idx = 0; - if (queue[1].type == parse_special_type_tokenizer_error) - { + if (queue[1].type == parse_special_type_tokenizer_error) { error_token_idx = (queue[1].type == parse_special_type_tokenizer_error ? 1 : 0); - token_count = -1; // so that it will be 0 after incrementing, and our tokenizer error will be ignored + token_count = -1; // so that it will be 0 after incrementing, and our tokenizer + // error will be ignored } - /* Mark a special error token, and then keep going */ - const parse_token_t token = {parse_special_type_parse_error, parse_keyword_none, false, false, queue[error_token_idx].source_start, queue[error_token_idx].source_length}; + // Mark a special error token, and then keep going. + const parse_token_t token = {parse_special_type_parse_error, + parse_keyword_none, + false, + false, + queue[error_token_idx].source_start, + queue[error_token_idx].source_length}; parser.accept_tokens(token, kInvalidToken); parser.reset_symbols(goal); - } - else - { - /* Bail out */ - break; + } else { + break; // bail out } } } - // Teach each node where its source range is + // Teach each node where its source range is. parser.determine_node_ranges(); - // Acquire the output from the parser + // Acquire the output from the parser. parser.acquire_output(output, errors); #if 0 @@ -1347,24 +1270,23 @@ bool parse_tree_from_string(const wcstring &str, parse_tree_flags_t parse_flags, fprintf(stderr, "%lu nodes, node size %lu, %lu bytes\n", output->size(), sizeof(parse_node_t), output->size() * sizeof(parse_node_t)); #endif - // Indicate if we had a fatal error - return ! parser.has_fatal_error(); + // Indicate if we had a fatal error. + return !parser.has_fatal_error(); } -const parse_node_t *parse_node_tree_t::get_child(const parse_node_t &parent, node_offset_t which, parse_token_type_t expected_type) const -{ +const parse_node_t *parse_node_tree_t::get_child(const parse_node_t &parent, node_offset_t which, + parse_token_type_t expected_type) const { const parse_node_t *result = NULL; - /* We may get nodes with no children if we had an incomplete parse. Don't consider than an error */ - if (parent.child_count > 0) - { + // We may get nodes with no children if we had an incomplete parse. Don't consider than an + // error. + if (parent.child_count > 0) { PARSE_ASSERT(which < parent.child_count); node_offset_t child_offset = parent.child_offset(which); - if (child_offset < this->size()) - { + if (child_offset < this->size()) { result = &this->at(child_offset); - /* If we are given an expected type, then the node must be null or that type */ + // If we are given an expected type, then the node must be null or that type. assert(expected_type == token_type_invalid || expected_type == result->type); } } @@ -1372,43 +1294,38 @@ const parse_node_t *parse_node_tree_t::get_child(const parse_node_t &parent, nod return result; } -const parse_node_t &parse_node_tree_t::find_child(const parse_node_t &parent, parse_token_type_t type) const -{ - for (node_offset_t i=0; i < parent.child_count; i++) - { +const parse_node_t &parse_node_tree_t::find_child(const parse_node_t &parent, + parse_token_type_t type) const { + for (node_offset_t i = 0; i < parent.child_count; i++) { const parse_node_t *child = this->get_child(parent, i); - if (child->type == type) - { + if (child->type == type) { return *child; } } PARSE_ASSERT(0); - return *(parse_node_t *)(NULL); //unreachable + return *(parse_node_t *)(NULL); // unreachable } -const parse_node_t *parse_node_tree_t::get_parent(const parse_node_t &node, parse_token_type_t expected_type) const -{ +const parse_node_t *parse_node_tree_t::get_parent(const parse_node_t &node, + parse_token_type_t expected_type) const { const parse_node_t *result = NULL; - if (node.parent != NODE_OFFSET_INVALID) - { + if (node.parent != NODE_OFFSET_INVALID) { PARSE_ASSERT(node.parent < this->size()); const parse_node_t &parent = this->at(node.parent); - if (expected_type == token_type_invalid || expected_type == parent.type) - { - // The type matches (or no type was requested) + if (expected_type == token_type_invalid || expected_type == parent.type) { + // The type matches (or no type was requested). result = &parent; } } return result; } -static void find_nodes_recursive(const parse_node_tree_t &tree, const parse_node_t &parent, parse_token_type_t type, parse_node_tree_t::parse_node_list_t *result, size_t max_count) -{ - if (result->size() < max_count) - { +static void find_nodes_recursive(const parse_node_tree_t &tree, const parse_node_t &parent, + parse_token_type_t type, + parse_node_tree_t::parse_node_list_t *result, size_t max_count) { + if (result->size() < max_count) { if (parent.type == type) result->push_back(&parent); - for (node_offset_t i=0; i < parent.child_count; i++) - { + for (node_offset_t i = 0; i < parent.child_count; i++) { const parse_node_t *child = tree.get_child(parent, i); assert(child != NULL); find_nodes_recursive(tree, *child, type, result, max_count); @@ -1416,46 +1333,38 @@ static void find_nodes_recursive(const parse_node_tree_t &tree, const parse_node } } -parse_node_tree_t::parse_node_list_t parse_node_tree_t::find_nodes(const parse_node_t &parent, parse_token_type_t type, size_t max_count) const -{ +parse_node_tree_t::parse_node_list_t parse_node_tree_t::find_nodes(const parse_node_t &parent, + parse_token_type_t type, + size_t max_count) const { parse_node_list_t result; find_nodes_recursive(*this, parent, type, &result, max_count); return result; } -/* Return true if the given node has the proposed ancestor as an ancestor (or is itself that ancestor) */ -static bool node_has_ancestor(const parse_node_tree_t &tree, const parse_node_t &node, const parse_node_t &proposed_ancestor) -{ - if (&node == &proposed_ancestor) - { - /* Found it */ - return true; - } - else if (node.parent == NODE_OFFSET_INVALID) - { - /* No more parents */ - return false; - } - else - { - /* Recurse to the parent */ +/// Return true if the given node has the proposed ancestor as an ancestor (or is itself that +/// ancestor). +static bool node_has_ancestor(const parse_node_tree_t &tree, const parse_node_t &node, + const parse_node_t &proposed_ancestor) { + if (&node == &proposed_ancestor) { + return true; // found it + } else if (node.parent == NODE_OFFSET_INVALID) { + return false; // no more parents + } else { + // Recurse to the parent. return node_has_ancestor(tree, tree.at(node.parent), proposed_ancestor); } } -const parse_node_t *parse_node_tree_t::find_last_node_of_type(parse_token_type_t type, const parse_node_t *parent) const -{ +const parse_node_t *parse_node_tree_t::find_last_node_of_type(parse_token_type_t type, + const parse_node_t *parent) const { const parse_node_t *result = NULL; - // Find nodes of the given type in the tree, working backwards + // Find nodes of the given type in the tree, working backwards. size_t idx = this->size(); - while (idx--) - { + while (idx--) { const parse_node_t &node = this->at(idx); - if (node.type == type) - { - // Types match. Check if it has the right parent - if (parent == NULL || node_has_ancestor(*this, node, *parent)) - { + if (node.type == type) { + // Types match. Check if it has the right parent. + if (parent == NULL || node_has_ancestor(*this, node, *parent)) { // Success result = &node; break; @@ -1465,99 +1374,87 @@ const parse_node_t *parse_node_tree_t::find_last_node_of_type(parse_token_type_t return result; } -const parse_node_t *parse_node_tree_t::find_node_matching_source_location(parse_token_type_t type, size_t source_loc, const parse_node_t *parent) const -{ +const parse_node_t *parse_node_tree_t::find_node_matching_source_location( + parse_token_type_t type, size_t source_loc, const parse_node_t *parent) const { const parse_node_t *result = NULL; - // Find nodes of the given type in the tree, working backwards + // Find nodes of the given type in the tree, working backwards. const size_t len = this->size(); - for (size_t idx=0; idx < len; idx++) - { + for (size_t idx = 0; idx < len; idx++) { const parse_node_t &node = this->at(idx); - /* Types must match */ - if (node.type != type) - continue; + // Types must match. + if (node.type != type) continue; - /* Must contain source location */ - if (! node.location_in_or_at_end_of_source_range(source_loc)) - continue; + // Must contain source location. + if (!node.location_in_or_at_end_of_source_range(source_loc)) continue; - /* If a parent is given, it must be an ancestor */ - if (parent != NULL && ! node_has_ancestor(*this, node, *parent)) - continue; + // If a parent is given, it must be an ancestor. + if (parent != NULL && !node_has_ancestor(*this, node, *parent)) continue; - /* Found it */ + // Found it. result = &node; break; } return result; } - -bool parse_node_tree_t::argument_list_is_root(const parse_node_t &node) const -{ +bool parse_node_tree_t::argument_list_is_root(const parse_node_t &node) const { bool result = true; assert(node.type == symbol_argument_list || node.type == symbol_arguments_or_redirections_list); const parse_node_t *parent = this->get_parent(node); - if (parent != NULL) - { - /* We have a parent - check to make sure it's not another list! */ - result = parent->type != symbol_arguments_or_redirections_list && parent->type != symbol_argument_list; + if (parent != NULL) { + // We have a parent - check to make sure it's not another list! + result = parent->type != symbol_arguments_or_redirections_list && + parent->type != symbol_argument_list; } return result; } -enum parse_statement_decoration_t parse_node_tree_t::decoration_for_plain_statement(const parse_node_t &node) const -{ +enum parse_statement_decoration_t parse_node_tree_t::decoration_for_plain_statement( + const parse_node_t &node) const { assert(node.type == symbol_plain_statement); - const parse_node_t *decorated_statement = this->get_parent(node, symbol_decorated_statement); - parse_node_tag_t tag = decorated_statement ? decorated_statement->tag : parse_statement_decoration_none; + const parse_node_t *decorated_statement = this->get_parent(node, symbol_decorated_statement); + parse_node_tag_t tag = + decorated_statement ? decorated_statement->tag : parse_statement_decoration_none; return static_cast(tag); } -bool parse_node_tree_t::command_for_plain_statement(const parse_node_t &node, const wcstring &src, wcstring *out_cmd) const -{ +bool parse_node_tree_t::command_for_plain_statement(const parse_node_t &node, const wcstring &src, + wcstring *out_cmd) const { bool result = false; assert(node.type == symbol_plain_statement); const parse_node_t *cmd_node = this->get_child(node, 0, parse_token_type_string); - if (cmd_node != NULL && cmd_node->has_source()) - { + if (cmd_node != NULL && cmd_node->has_source()) { out_cmd->assign(src, cmd_node->source_start, cmd_node->source_length); result = true; - } - else - { + } else { out_cmd->clear(); } return result; } -bool parse_node_tree_t::statement_is_in_pipeline(const parse_node_t &node, bool include_first) const -{ - // Moderately nasty hack! Walk up our ancestor chain and see if we are in a job_continuation. This checks if we are in the second or greater element in a pipeline; if we are the first element we treat this as false - // This accepts a few statement types +bool parse_node_tree_t::statement_is_in_pipeline(const parse_node_t &node, + bool include_first) const { + // Moderately nasty hack! Walk up our ancestor chain and see if we are in a job_continuation. + // This checks if we are in the second or greater element in a pipeline; if we are the first + // element we treat this as false. This accepts a few statement types. bool result = false; const parse_node_t *ancestor = &node; - // If we're given a plain statement, try to get its decorated statement parent + // If we're given a plain statement, try to get its decorated statement parent. if (ancestor && ancestor->type == symbol_plain_statement) ancestor = this->get_parent(*ancestor, symbol_decorated_statement); - if (ancestor) - ancestor = this->get_parent(*ancestor, symbol_statement); - if (ancestor) - ancestor = this->get_parent(*ancestor); + if (ancestor) ancestor = this->get_parent(*ancestor, symbol_statement); + if (ancestor) ancestor = this->get_parent(*ancestor); - if (ancestor) - { - if (ancestor->type == symbol_job_continuation) - { - // Second or more in a pipeline + if (ancestor) { + if (ancestor->type == symbol_job_continuation) { + // Second or more in a pipeline. result = true; - } - else if (ancestor->type == symbol_job && include_first) - { - // Check to see if we have a job continuation that's not empty - const parse_node_t *continuation = this->get_child(*ancestor, 1, symbol_job_continuation); + } else if (ancestor->type == symbol_job && include_first) { + // Check to see if we have a job continuation that's not empty. + const parse_node_t *continuation = + this->get_child(*ancestor, 1, symbol_job_continuation); result = (continuation != NULL && continuation->child_count > 0); } } @@ -1565,57 +1462,55 @@ bool parse_node_tree_t::statement_is_in_pipeline(const parse_node_t &node, bool return result; } -enum token_type parse_node_tree_t::type_for_redirection(const parse_node_t &redirection_node, const wcstring &src, int *out_fd, wcstring *out_target) const -{ +enum token_type parse_node_tree_t::type_for_redirection(const parse_node_t &redirection_node, + const wcstring &src, int *out_fd, + wcstring *out_target) const { assert(redirection_node.type == symbol_redirection); enum token_type result = TOK_NONE; - const parse_node_t *redirection_primitive = this->get_child(redirection_node, 0, parse_token_type_redirection); //like 2> - const parse_node_t *redirection_target = this->get_child(redirection_node, 1, parse_token_type_string); //like &1 or file path + const parse_node_t *redirection_primitive = + this->get_child(redirection_node, 0, parse_token_type_redirection); // like 2> + const parse_node_t *redirection_target = + this->get_child(redirection_node, 1, parse_token_type_string); // like &1 or file path - if (redirection_primitive != NULL && redirection_primitive->has_source()) - { + if (redirection_primitive != NULL && redirection_primitive->has_source()) { result = redirection_type_for_string(redirection_primitive->get_source(src), out_fd); } - if (out_target != NULL) - { + if (out_target != NULL) { *out_target = redirection_target ? redirection_target->get_source(src) : L""; } return result; } -const parse_node_t *parse_node_tree_t::header_node_for_block_statement(const parse_node_t &node) const -{ +const parse_node_t *parse_node_tree_t::header_node_for_block_statement( + const parse_node_t &node) const { const parse_node_t *result = NULL; - if (node.type == symbol_block_statement) - { + if (node.type == symbol_block_statement) { const parse_node_t *block_header = this->get_child(node, 0, symbol_block_header); - if (block_header != NULL) - { + if (block_header != NULL) { result = this->get_child(*block_header, 0); } } return result; } -parse_node_tree_t::parse_node_list_t parse_node_tree_t::specific_statements_for_job(const parse_node_t &job) const -{ +parse_node_tree_t::parse_node_list_t parse_node_tree_t::specific_statements_for_job( + const parse_node_t &job) const { assert(job.type == symbol_job); parse_node_list_t result; - /* Initial statement (non-specific) */ + // Initial statement (non-specific). result.push_back(get_child(job, 0, symbol_statement)); - /* Our cursor variable. Walk over the list of continuations. */ + // Our cursor variable. Walk over the list of continuations. const parse_node_t *continuation = get_child(job, 1, symbol_job_continuation); - while (continuation != NULL && continuation->child_count > 0) - { + while (continuation != NULL && continuation->child_count > 0) { result.push_back(get_child(*continuation, 1, symbol_statement)); continuation = get_child(*continuation, 2, symbol_job_continuation); } - /* Result now contains a list of statements. But we want a list of specific statements e.g. symbol_switch_statement. So replace them in-place in the vector. */ - for (size_t i=0; i < result.size(); i++) - { + // Result now contains a list of statements. But we want a list of specific statements e.g. + // symbol_switch_statement. So replace them in-place in the vector. + for (size_t i = 0; i < result.size(); i++) { const parse_node_t *statement = result.at(i); assert(statement->type == symbol_statement); result.at(i) = this->get_child(*statement, 0); @@ -1624,17 +1519,15 @@ parse_node_tree_t::parse_node_list_t parse_node_tree_t::specific_statements_for_ return result; } -parse_node_tree_t::parse_node_list_t parse_node_tree_t::comment_nodes_for_node(const parse_node_t &parent) const -{ +parse_node_tree_t::parse_node_list_t parse_node_tree_t::comment_nodes_for_node( + const parse_node_t &parent) const { parse_node_list_t result; - if (parent.has_comments()) - { - /* Walk all our nodes, looking for comment nodes that have the given node as a parent */ - for (size_t i=0; i < this->size(); i++) - { + if (parent.has_comments()) { + // Walk all our nodes, looking for comment nodes that have the given node as a parent. + for (size_t i = 0; i < this->size(); i++) { const parse_node_t &potential_comment = this->at(i); - if (potential_comment.type == parse_special_type_comment && this->get_parent(potential_comment) == &parent) - { + if (potential_comment.type == parse_special_type_comment && + this->get_parent(potential_comment) == &parent) { result.push_back(&potential_comment); } } @@ -1642,58 +1535,53 @@ parse_node_tree_t::parse_node_list_t parse_node_tree_t::comment_nodes_for_node(c return result; } -enum parse_bool_statement_type_t parse_node_tree_t::statement_boolean_type(const parse_node_t &node) -{ +enum parse_bool_statement_type_t parse_node_tree_t::statement_boolean_type( + const parse_node_t &node) { assert(node.type == symbol_boolean_statement); return static_cast(node.tag); } -bool parse_node_tree_t::job_should_be_backgrounded(const parse_node_t &job) const -{ +bool parse_node_tree_t::job_should_be_backgrounded(const parse_node_t &job) const { assert(job.type == symbol_job); const parse_node_t *opt_background = get_child(job, 2, symbol_optional_background); bool result = opt_background != NULL && opt_background->tag == parse_background; return result; } -const parse_node_t *parse_node_tree_t::next_node_in_node_list(const parse_node_t &node_list, parse_token_type_t entry_type, const parse_node_t **out_list_tail) const -{ +const parse_node_t *parse_node_tree_t::next_node_in_node_list( + const parse_node_t &node_list, parse_token_type_t entry_type, + const parse_node_t **out_list_tail) const { parse_token_type_t list_type = node_list.type; - /* Paranoia - it doesn't make sense for a list type to contain itself */ + // Paranoia - it doesn't make sense for a list type to contain itself. assert(list_type != entry_type); const parse_node_t *list_cursor = &node_list; const parse_node_t *list_entry = NULL; - /* Loop while we don't have an item but do have a list. Note that some nodes may contain nothing - e.g. job_list contains blank lines as a production */ - while (list_entry == NULL && list_cursor != NULL) - { + // Loop while we don't have an item but do have a list. Note that some nodes may contain + // nothing; e.g. job_list contains blank lines as a production. + while (list_entry == NULL && list_cursor != NULL) { const parse_node_t *next_cursor = NULL; - /* Walk through the children */ - for (node_offset_t i=0; i < list_cursor->child_count; i++) - { + // Walk through the children. + for (node_offset_t i = 0; i < list_cursor->child_count; i++) { const parse_node_t *child = this->get_child(*list_cursor, i); - if (child->type == entry_type) - { - /* This is the list entry */ + if (child->type == entry_type) { + // This is the list entry. list_entry = child; - } - else if (child->type == list_type) - { - /* This is the next in the list */ + } else if (child->type == list_type) { + // This is the next in the list. next_cursor = child; } } - /* Go to the next entry, even if it's NULL */ + // Go to the next entry, even if it's NULL. list_cursor = next_cursor; } - /* Return what we got */ + // Return what we got. assert(list_cursor == NULL || list_cursor->type == list_type); assert(list_entry == NULL || list_entry->type == entry_type); - if (out_list_tail != NULL) - *out_list_tail = list_cursor; + if (out_list_tail != NULL) *out_list_tail = list_cursor; return list_entry; } diff --git a/src/parse_tree.h b/src/parse_tree.h index 9fd176450..4ec98ae83 100644 --- a/src/parse_tree.h +++ b/src/parse_tree.h @@ -1,20 +1,17 @@ -/**\file parse_tree.h - - Programmatic representation of fish code. -*/ +// Programmatic representation of fish code. #ifndef FISH_PARSE_PRODUCTIONS_H #define FISH_PARSE_PRODUCTIONS_H #include -#include -#include -#include -#include #include +#include +#include +#include +#include #include "common.h" -#include "tokenizer.h" #include "parse_constants.h" +#include "tokenizer.h" class parse_node_tree_t; @@ -26,13 +23,12 @@ typedef uint32_t source_offset_t; #define SOURCE_OFFSET_INVALID (static_cast(-1)) -/** A struct representing the token type that we use internally */ -struct parse_token_t -{ - enum parse_token_type_t type; // The type of the token as represented by the parser - enum parse_keyword_t keyword; // Any keyword represented by this token - bool has_dash_prefix; // Hackish: whether the source contains a dash prefix - bool is_help_argument; // Hackish: whether the source looks like '-h' or '--help' +/// A struct representing the token type that we use internally. +struct parse_token_t { + enum parse_token_type_t type; // The type of the token as represented by the parser + enum parse_keyword_t keyword; // Any keyword represented by this token + bool has_dash_prefix; // Hackish: whether the source contains a dash prefix + bool is_help_argument; // Hackish: whether the source looks like '-h' or '--help' source_offset_t source_start; source_offset_t source_length; @@ -40,24 +36,20 @@ struct parse_token_t wcstring user_presentable_description() const; }; - -enum -{ +enum { parse_flag_none = 0, - /* Attempt to build a "parse tree" no matter what. This may result in a 'forest' of disconnected trees. This is intended to be used by syntax highlighting. */ + /// Attempt to build a "parse tree" no matter what. This may result in a 'forest' of + /// disconnected trees. This is intended to be used by syntax highlighting. parse_flag_continue_after_error = 1 << 0, - - /* Include comment tokens */ + /// Include comment tokens. parse_flag_include_comments = 1 << 1, - - /* Indicate that the tokenizer should accept incomplete tokens */ + /// Indicate that the tokenizer should accept incomplete tokens */ parse_flag_accept_incomplete_tokens = 1 << 2, - - /* Indicate that the parser should not generate the terminate token, allowing an 'unfinished' tree where some nodes may have no productions. */ + /// Indicate that the parser should not generate the terminate token, allowing an 'unfinished' + /// tree where some nodes may have no productions. parse_flag_leave_unterminated = 1 << 3, - - /* Indicate that the parser should generate job_list entries for blank lines. */ + /// Indicate that the parser should generate job_list entries for blank lines. parse_flag_show_blank_lines = 1 << 4 }; typedef unsigned int parse_tree_flags_t; @@ -67,247 +59,259 @@ wcstring parse_dump_tree(const parse_node_tree_t &tree, const wcstring &src); const wchar_t *token_type_description(parse_token_type_t type); const wchar_t *keyword_description(parse_keyword_t type); -/* Node flags */ -enum -{ - /* Flag indicating that the node has associated comment nodes */ +// Node flags. +enum { + /// Flag indicating that the node has associated comment nodes. parse_node_flag_has_comments = 1 << 0, }; typedef uint8_t parse_node_flags_t; -/* Node-type specific tag value */ +/// Node-type specific tag value. typedef uint8_t parse_node_tag_t; -/** Class for nodes of a parse tree. Since there's a lot of these, the size and order of the fields is important. */ -class parse_node_t -{ -public: - /* Start in the source code */ +/// Class for nodes of a parse tree. Since there's a lot of these, the size and order of the fields +/// is important. +class parse_node_t { + public: + // Start in the source code. source_offset_t source_start; - - /* Length of our range in the source code */ + // Length of our range in the source code. source_offset_t source_length; - - /* Parent */ + // Parent node_offset_t parent; - - /* Children */ + // Children node_offset_t child_start; - - /* Number of children */ + // Number of children. uint8_t child_count; - - /* Type of the node */ + // Type of the node. enum parse_token_type_t type; - - /* Keyword associated with node */ + // Keyword associated with node. enum parse_keyword_t keyword; - - /* Node flags */ - parse_node_flags_t flags:4; - - /* This is used to store e.g. the statement decoration. */ - parse_node_tag_t tag:4; - - /* Description */ + // Node flags. + parse_node_flags_t flags : 4; + // This is used to store e.g. the statement decoration. + parse_node_tag_t tag : 4; + // Description wcstring describe(void) const; - /* Constructor */ - explicit parse_node_t(parse_token_type_t ty) : - source_start(SOURCE_OFFSET_INVALID), - source_length(0), - parent(NODE_OFFSET_INVALID), - child_start(0), - child_count(0), - type(ty), - keyword(parse_keyword_none), - flags(0), - tag(0) - { - } + // Constructor + explicit parse_node_t(parse_token_type_t ty) + : source_start(SOURCE_OFFSET_INVALID), + source_length(0), + parent(NODE_OFFSET_INVALID), + child_start(0), + child_count(0), + type(ty), + keyword(parse_keyword_none), + flags(0), + tag(0) {} - node_offset_t child_offset(node_offset_t which) const - { + node_offset_t child_offset(node_offset_t which) const { PARSE_ASSERT(which < child_count); return child_start + which; } - /* Indicate if this node has a range of source code associated with it */ - bool has_source() const - { - /* Should never have a nonempty range with an invalid offset */ + /// Indicate if this node has a range of source code associated with it. + bool has_source() const { + // Should never have a nonempty range with an invalid offset. assert(this->source_start != SOURCE_OFFSET_INVALID || this->source_length == 0); return this->source_length > 0; } - /* Indicate if the node has comment nodes */ - bool has_comments() const - { - return !! (this->flags & parse_node_flag_has_comments); - } + /// Indicate if the node has comment nodes. + bool has_comments() const { return !!(this->flags & parse_node_flag_has_comments); } - /* Gets source for the node, or the empty string if it has no source */ - wcstring get_source(const wcstring &str) const - { - if (! has_source()) + /// Gets source for the node, or the empty string if it has no source. + wcstring get_source(const wcstring &str) const { + if (!has_source()) return wcstring(); else return wcstring(str, this->source_start, this->source_length); } - /* Returns whether the given location is within the source range or at its end */ - bool location_in_or_at_end_of_source_range(size_t loc) const - { + /// Returns whether the given location is within the source range or at its end. + bool location_in_or_at_end_of_source_range(size_t loc) const { return has_source() && source_start <= loc && loc - source_start <= source_length; } }; -/* The parse tree itself */ -class parse_node_tree_t : public std::vector -{ -public: - +/// The parse tree itself. +class parse_node_tree_t : public std::vector { + public: parse_node_tree_t() {} - - parse_node_tree_t(moved_ref t) - { - this->swap(t.val); - } - /* Get the node corresponding to a child of the given node, or NULL if there is no such child. If expected_type is provided, assert that the node has that type. - */ - const parse_node_t *get_child(const parse_node_t &parent, node_offset_t which, parse_token_type_t expected_type = token_type_invalid) const; + parse_node_tree_t(moved_ref t) { this->swap(t.val); } - /* Find the first direct child of the given node of the given type. asserts on failure - */ + // Get the node corresponding to a child of the given node, or NULL if there is no such child. + // If expected_type is provided, assert that the node has that type. + const parse_node_t *get_child(const parse_node_t &parent, node_offset_t which, + parse_token_type_t expected_type = token_type_invalid) const; + + // Find the first direct child of the given node of the given type. asserts on failure. const parse_node_t &find_child(const parse_node_t &parent, parse_token_type_t type) const; - /* Get the node corresponding to the parent of the given node, or NULL if there is no such child. If expected_type is provided, only returns the parent if it is of that type. Note the asymmetry: get_child asserts since the children are known, but get_parent does not, since the parent may not be known. */ - const parse_node_t *get_parent(const parse_node_t &node, parse_token_type_t expected_type = token_type_invalid) const; + // Get the node corresponding to the parent of the given node, or NULL if there is no such + // child. If expected_type is provided, only returns the parent if it is of that type. Note the + // asymmetry: get_child asserts since the children are known, but get_parent does not, since the + // parent may not be known. + const parse_node_t *get_parent(const parse_node_t &node, + parse_token_type_t expected_type = token_type_invalid) const; - /* Find all the nodes of a given type underneath a given node, up to max_count of them */ + // Find all the nodes of a given type underneath a given node, up to max_count of them. typedef std::vector parse_node_list_t; - parse_node_list_t find_nodes(const parse_node_t &parent, parse_token_type_t type, size_t max_count = (size_t)(-1)) const; + parse_node_list_t find_nodes(const parse_node_t &parent, parse_token_type_t type, + size_t max_count = (size_t)(-1)) const; - /* Finds the last node of a given type underneath a given node, or NULL if it could not be found. If parent is NULL, this finds the last node in the tree of that type. */ - const parse_node_t *find_last_node_of_type(parse_token_type_t type, const parse_node_t *parent = NULL) const; + // Finds the last node of a given type underneath a given node, or NULL if it could not be + // found. If parent is NULL, this finds the last node in the tree of that type. + const parse_node_t *find_last_node_of_type(parse_token_type_t type, + const parse_node_t *parent = NULL) const; - /* Finds a node containing the given source location. If 'parent' is not NULL, it must be an ancestor. */ - const parse_node_t *find_node_matching_source_location(parse_token_type_t type, size_t source_loc, const parse_node_t *parent) const; + // Finds a node containing the given source location. If 'parent' is not NULL, it must be an + // ancestor. + const parse_node_t *find_node_matching_source_location(parse_token_type_t type, + size_t source_loc, + const parse_node_t *parent) const; - /* Indicate if the given argument_list or arguments_or_redirections_list is a root list, or has a parent */ + // Indicate if the given argument_list or arguments_or_redirections_list is a root list, or has + // a parent. bool argument_list_is_root(const parse_node_t &node) const; - /* Utilities */ + // Utilities - /* Given a plain statement, get the decoration (from the parent node), or none if there is no decoration */ - enum parse_statement_decoration_t decoration_for_plain_statement(const parse_node_t &node) const; + /// Given a plain statement, get the decoration (from the parent node), or none if there is no + /// decoration. + enum parse_statement_decoration_t decoration_for_plain_statement( + const parse_node_t &node) const; - /* Given a plain statement, get the command by reference (from the child node). Returns true if successful. Clears the command on failure. */ - bool command_for_plain_statement(const parse_node_t &node, const wcstring &src, wcstring *out_cmd) const; + /// Given a plain statement, get the command by reference (from the child node). Returns true if + /// successful. Clears the command on failure. + bool command_for_plain_statement(const parse_node_t &node, const wcstring &src, + wcstring *out_cmd) const; - /* Given a plain statement, return true if the statement is part of a pipeline. If include_first is set, the first command in a pipeline is considered part of it; otherwise only the second or additional commands are */ + /// Given a plain statement, return true if the statement is part of a pipeline. If + /// include_first is set, the first command in a pipeline is considered part of it; otherwise + /// only the second or additional commands are. bool statement_is_in_pipeline(const parse_node_t &node, bool include_first) const; - /* Given a redirection, get the redirection type (or TOK_NONE) and target (file path, or fd) */ - enum token_type type_for_redirection(const parse_node_t &node, const wcstring &src, int *out_fd, wcstring *out_target) const; + /// Given a redirection, get the redirection type (or TOK_NONE) and target (file path, or fd). + enum token_type type_for_redirection(const parse_node_t &node, const wcstring &src, int *out_fd, + wcstring *out_target) const; - /* If the given node is a block statement, returns the header node (for_header, while_header, begin_header, or function_header). Otherwise returns NULL */ + /// If the given node is a block statement, returns the header node (for_header, while_header, + /// begin_header, or function_header). Otherwise returns NULL. const parse_node_t *header_node_for_block_statement(const parse_node_t &node) const; - /* Given a node list (e.g. of type symbol_job_list) and a node type (e.g. symbol_job), return the next element of the given type in that list, and the tail (by reference). Returns NULL if we've exhausted the list. */ - const parse_node_t *next_node_in_node_list(const parse_node_t &node_list, parse_token_type_t item_type, const parse_node_t **list_tail) const; + /// Given a node list (e.g. of type symbol_job_list) and a node type (e.g. symbol_job), return + /// the next element of the given type in that list, and the tail (by reference). Returns NULL + /// if we've exhausted the list. + const parse_node_t *next_node_in_node_list(const parse_node_t &node_list, + parse_token_type_t item_type, + const parse_node_t **list_tail) const; - /* Given a job, return all of its statements. These are 'specific statements' (e.g. symbol_decorated_statement, not symbol_statement) */ + /// Given a job, return all of its statements. These are 'specific statements' (e.g. + /// symbol_decorated_statement, not symbol_statement). parse_node_list_t specific_statements_for_job(const parse_node_t &job) const; - /* Given a node, return all of its comment nodes. */ + /// Given a node, return all of its comment nodes. parse_node_list_t comment_nodes_for_node(const parse_node_t &node) const; - /* Returns the boolean type for a boolean node */ + /// Returns the boolean type for a boolean node. static enum parse_bool_statement_type_t statement_boolean_type(const parse_node_t &node); - /* Given a job, return whether it should be backgrounded, because it has a & specifier */ + /// Given a job, return whether it should be backgrounded, because it has a & specifier. bool job_should_be_backgrounded(const parse_node_t &job) const; }; -/* The big entry point. Parse a string, attempting to produce a tree for the given goal type */ -bool parse_tree_from_string(const wcstring &str, parse_tree_flags_t flags, parse_node_tree_t *output, parse_error_list_t *errors, parse_token_type_t goal = symbol_job_list); +/// The big entry point. Parse a string, attempting to produce a tree for the given goal type. +bool parse_tree_from_string(const wcstring &str, parse_tree_flags_t flags, + parse_node_tree_t *output, parse_error_list_t *errors, + parse_token_type_t goal = symbol_job_list); -/* Fish grammar: - -# A job_list is a list of jobs, separated by semicolons or newlines - - job_list = | - job job_list | - job_list - -# A job is a non-empty list of statements, separated by pipes. (Non-empty is useful for cases like if statements, where we require a command). To represent "non-empty", we require a statement, followed by a possibly empty job_continuation, and then optionally a background specifier '&' - - job = statement job_continuation optional_background - job_continuation = | - statement job_continuation - -# A statement is a normal command, or an if / while / and etc - - statement = boolean_statement | block_statement | if_statement | switch_statement | decorated_statement - -# A block is a conditional, loop, or begin/end - - if_statement = if_clause else_clause end_command arguments_or_redirections_list - if_clause = job andor_job_list job_list - else_clause = | - else_continuation - else_continuation = if_clause else_clause | - job_list - - switch_statement = SWITCH argument case_item_list end_command arguments_or_redirections_list - case_item_list = | - case_item case_item_list | - case_item_list - - case_item = CASE argument_list job_list - - block_statement = block_header job_list end_command arguments_or_redirections_list - block_header = for_header | while_header | function_header | begin_header - for_header = FOR var_name IN argument_list - while_header = WHILE job andor_job_list - begin_header = BEGIN - -# Functions take arguments, and require at least one (the name). No redirections allowed. - function_header = FUNCTION argument argument_list - -# A boolean statement is AND or OR or NOT - boolean_statement = AND statement | OR statement | NOT statement - -# An andor_job_list is zero or more job lists, where each starts with an `and` or `or` boolean statement - andor_job_list = | - job andor_job_list | - andor_job_list - -# A decorated_statement is a command with a list of arguments_or_redirections, possibly with "builtin" or "command" or "exec" - - decorated_statement = plain_statement | COMMAND plain_statement | BUILTIN plain_statement | EXEC plain_statement - plain_statement = arguments_or_redirections_list - - argument_list = | argument argument_list - - arguments_or_redirections_list = | - argument_or_redirection arguments_or_redirections_list - argument_or_redirection = argument | redirection - argument = - - redirection = - - optional_background = | - - end_command = END - - # A freestanding_argument_list is equivalent to a normal argument list, except it may contain TOK_END (newlines, and even semicolons, for historical reasons - - freestanding_argument_list = | - argument freestanding_argument_list | - freestanding_argument_list -*/ +// Fish grammar: +// +// # A job_list is a list of jobs, separated by semicolons or newlines +// +// job_list = | +// job job_list | +// job_list +// +// # A job is a non-empty list of statements, separated by pipes. (Non-empty is useful for cases +// like if statements, where we require a command). To represent "non-empty", we require a +// statement, followed by a possibly empty job_continuation, and then optionally a background +// specifier '&' +// +// job = statement job_continuation optional_background +// job_continuation = | +// statement job_continuation +// +// # A statement is a normal command, or an if / while / and etc +// +// statement = boolean_statement | block_statement | if_statement | switch_statement | +// decorated_statement +// +// # A block is a conditional, loop, or begin/end +// +// if_statement = if_clause else_clause end_command arguments_or_redirections_list +// if_clause = job andor_job_list job_list +// else_clause = | +// else_continuation +// else_continuation = if_clause else_clause | +// job_list +// +// switch_statement = SWITCH argument case_item_list end_command +// arguments_or_redirections_list +// case_item_list = | +// case_item case_item_list | +// case_item_list +// +// case_item = CASE argument_list job_list +// +// block_statement = block_header job_list end_command arguments_or_redirections_list +// block_header = for_header | while_header | function_header | begin_header +// for_header = FOR var_name IN argument_list +// while_header = WHILE job andor_job_list +// begin_header = BEGIN +// +// # Functions take arguments, and require at least one (the name). No redirections allowed. +// function_header = FUNCTION argument argument_list +// +// # A boolean statement is AND or OR or NOT +// boolean_statement = AND statement | OR statement | NOT statement +// +// # An andor_job_list is zero or more job lists, where each starts with an `and` or `or` boolean +// statement +// andor_job_list = | +// job andor_job_list | +// andor_job_list +// +// # A decorated_statement is a command with a list of arguments_or_redirections, possibly with +// "builtin" or "command" or "exec" +// +// decorated_statement = plain_statement | COMMAND plain_statement | BUILTIN plain_statement | +// EXEC +// +// plain_statement +// plain_statement = arguments_or_redirections_list +// +// argument_list = | argument argument_list +// +// arguments_or_redirections_list = | +// argument_or_redirection arguments_or_redirections_list +// argument_or_redirection = argument | redirection +// argument = +// +// redirection = +// +// optional_background = | +// +// end_command = END +// +// # A freestanding_argument_list is equivalent to a normal argument list, except it may contain +// TOK_END (newlines, and even semicolons, for historical reasons +// +// freestanding_argument_list = | +// argument freestanding_argument_list | +// freestanding_argument_list #endif From ca912f157ea9a6ba8690c83eecb72f1c50b23193 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Mon, 2 May 2016 16:53:10 -0700 Subject: [PATCH 201/363] restyle parse_util module to match project style Reduces lint errors from 187 to 91 (-51%). Line count from 1754 to 1477 (-16%). Another step in resolving issue #2902. --- src/parse_util.cpp | 1550 +++++++++++++++++++------------------------- src/parse_util.h | 249 +++---- 2 files changed, 761 insertions(+), 1038 deletions(-) diff --git a/src/parse_util.cpp b/src/parse_util.cpp index 5d6721ec4..85016244b 100644 --- a/src/parse_util.cpp +++ b/src/parse_util.cpp @@ -1,329 +1,283 @@ -/** \file parse_util.c - - Various mostly unrelated utility functions related to parsing, - loading and evaluating fish code. - - This library can be seen as a 'toolbox' for functions that are - used in many places in fish and that are somehow related to - parsing the code. -*/ -#include -#include -#include -#include +// Various mostly unrelated utility functions related to parsing, loading and evaluating fish code. +// +// This library can be seen as a 'toolbox' for functions that are used in many places in fish and +// that are somehow related to parsing the code. #include -#include -#include +#include #include +#include +#include +#include +#include +#include -#include "fallback.h" // IWYU pragma: keep -#include "util.h" -#include "wutil.h" // IWYU pragma: keep -#include "common.h" -#include "tokenizer.h" -#include "parse_util.h" -#include "expand.h" -#include "wildcard.h" -#include "parse_tree.h" #include "builtin.h" +#include "common.h" +#include "expand.h" +#include "fallback.h" // IWYU pragma: keep #include "parse_constants.h" +#include "parse_tree.h" +#include "parse_util.h" +#include "tokenizer.h" +#include "util.h" +#include "wildcard.h" +#include "wutil.h" // IWYU pragma: keep -/** Error message for improper use of the exec builtin */ +/// Error message for improper use of the exec builtin. #define EXEC_ERR_MSG _(L"The '%ls' command can not be used in a pipeline") -/** Error message for use of backgrounded commands before and/or */ -#define BOOL_AFTER_BACKGROUND_ERROR_MSG _(L"The '%ls' command can not be used immediately after a backgrounded job") +/// Error message for use of backgrounded commands before and/or. +#define BOOL_AFTER_BACKGROUND_ERROR_MSG \ + _(L"The '%ls' command can not be used immediately after a backgrounded job") -/** Error message for backgrounded commands as conditionals */ -#define BACKGROUND_IN_CONDITIONAL_ERROR_MSG _(L"Backgrounded commands can not be used as conditionals") +/// Error message for backgrounded commands as conditionals. +#define BACKGROUND_IN_CONDITIONAL_ERROR_MSG \ + _(L"Backgrounded commands can not be used as conditionals") - -int parse_util_lineno(const wchar_t *str, size_t offset) -{ - if (! str) - return 0; +int parse_util_lineno(const wchar_t *str, size_t offset) { + if (!str) return 0; int res = 1; - for (size_t i=0; i < offset && str[i] != L'\0'; i++) - { - if (str[i] == L'\n') - { + for (size_t i = 0; i < offset && str[i] != L'\0'; i++) { + if (str[i] == L'\n') { res++; } } return res; } - -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) { const wchar_t *buff = str.c_str(); int count = 0; - for (size_t i=0; i= off2-off-1) - { - line_offset2 = off2-off-1; + if (line_offset2 >= off2 - off - 1) { + line_offset2 = off2 - off - 1; } return off + line_offset2; } -static int parse_util_locate_brackets_of_type(const wchar_t *in, wchar_t **begin, wchar_t **end, bool allow_incomplete, wchar_t open_type, wchar_t close_type) -{ - /* open_type is typically ( or [, and close type is the corresponding value */ +static int parse_util_locate_brackets_of_type(const wchar_t *in, wchar_t **begin, wchar_t **end, + bool allow_incomplete, wchar_t open_type, + wchar_t close_type) { + // open_type is typically ( or [, and close type is the corresponding value. wchar_t *pos; - wchar_t prev=0; - int syntax_error=0; - int paran_count=0; + wchar_t prev = 0; + int syntax_error = 0; + int paran_count = 0; - wchar_t *paran_begin=0, *paran_end=0; + wchar_t *paran_begin = 0, *paran_end = 0; CHECK(in, 0); - for (pos = const_cast(in); *pos; pos++) - { - if (prev != '\\') - { - if (wcschr(L"\'\"", *pos)) - { + for (pos = const_cast(in); *pos; pos++) { + if (prev != '\\') { + if (wcschr(L"\'\"", *pos)) { wchar_t *q_end = quote_end(pos); - if (q_end && *q_end) - { - pos=q_end; - } - else - { + if (q_end && *q_end) { + pos = q_end; + } else { break; } - } - else - { - if (*pos == open_type) - { - if ((paran_count == 0)&&(paran_begin==0)) - { + } else { + if (*pos == open_type) { + if ((paran_count == 0) && (paran_begin == 0)) { paran_begin = pos; } paran_count++; - } - else if (*pos == close_type) - { - + } else if (*pos == close_type) { paran_count--; - if ((paran_count == 0) && (paran_end == 0)) - { + if ((paran_count == 0) && (paran_end == 0)) { paran_end = pos; break; } - if (paran_count < 0) - { + if (paran_count < 0) { syntax_error = 1; break; } } } - } prev = *pos; } syntax_error |= (paran_count < 0); - syntax_error |= ((paran_count>0)&&(!allow_incomplete)); + syntax_error |= ((paran_count > 0) && (!allow_incomplete)); - if (syntax_error) - { + if (syntax_error) { return -1; } - if (paran_begin == 0) - { + if (paran_begin == 0) { return 0; } - if (begin) - { + if (begin) { *begin = paran_begin; } - if (end) - { - *end = paran_count?(wchar_t *)in+wcslen(in):paran_end; + if (end) { + *end = paran_count ? (wchar_t *)in + wcslen(in) : paran_end; } return 1; } - -int parse_util_locate_cmdsubst(const wchar_t *in, wchar_t **begin, wchar_t **end, bool accept_incomplete) -{ +int parse_util_locate_cmdsubst(const wchar_t *in, wchar_t **begin, wchar_t **end, + bool accept_incomplete) { return parse_util_locate_brackets_of_type(in, begin, end, accept_incomplete, L'(', L')'); } -int parse_util_locate_slice(const wchar_t *in, wchar_t **begin, wchar_t **end, bool accept_incomplete) -{ +int parse_util_locate_slice(const wchar_t *in, wchar_t **begin, wchar_t **end, + bool accept_incomplete) { return parse_util_locate_brackets_of_type(in, begin, end, accept_incomplete, L'[', L']'); } - -static int parse_util_locate_brackets_range(const wcstring &str, size_t *inout_cursor_offset, wcstring *out_contents, size_t *out_start, size_t *out_end, bool accept_incomplete, wchar_t open_type, wchar_t close_type) -{ - /* Clear the return values */ +static int parse_util_locate_brackets_range(const wcstring &str, size_t *inout_cursor_offset, + wcstring *out_contents, size_t *out_start, + size_t *out_end, bool accept_incomplete, + wchar_t open_type, wchar_t close_type) { + // Clear the return values. out_contents->clear(); *out_start = 0; *out_end = str.size(); - /* Nothing to do if the offset is at or past the end of the string. */ - if (*inout_cursor_offset >= str.size()) - return 0; + // Nothing to do if the offset is at or past the end of the string. + if (*inout_cursor_offset >= str.size()) return 0; - /* Defer to the wonky version */ - const wchar_t * const buff = str.c_str(); - const wchar_t * const valid_range_start = buff + *inout_cursor_offset, *valid_range_end = buff + str.size(); + // Defer to the wonky version. + const wchar_t *const buff = str.c_str(); + const wchar_t *const valid_range_start = buff + *inout_cursor_offset, + *valid_range_end = buff + str.size(); wchar_t *bracket_range_begin = NULL, *bracket_range_end = NULL; - int ret = parse_util_locate_brackets_of_type(valid_range_start, &bracket_range_begin, &bracket_range_end, accept_incomplete, open_type, close_type); - if (ret > 0) - { - /* The command substitutions must not be NULL and must be in the valid pointer range, and the end must be bigger than the beginning */ - assert(bracket_range_begin != NULL && bracket_range_begin >= valid_range_start && bracket_range_begin <= valid_range_end); - assert(bracket_range_end != NULL && bracket_range_end > bracket_range_begin && bracket_range_end >= valid_range_start && bracket_range_end <= valid_range_end); + int ret = parse_util_locate_brackets_of_type(valid_range_start, &bracket_range_begin, + &bracket_range_end, accept_incomplete, open_type, + close_type); + if (ret > 0) { + // The command substitutions must not be NULL and must be in the valid pointer range, and + // the end must be bigger than the beginning. + assert(bracket_range_begin != NULL && bracket_range_begin >= valid_range_start && + bracket_range_begin <= valid_range_end); + assert(bracket_range_end != NULL && bracket_range_end > bracket_range_begin && + bracket_range_end >= valid_range_start && bracket_range_end <= valid_range_end); - /* Assign the substring to the out_contents */ + // Assign the substring to the out_contents. const wchar_t *interior_begin = bracket_range_begin + 1; out_contents->assign(interior_begin, bracket_range_end - interior_begin); - /* Return the start and end */ + // Return the start and end. *out_start = bracket_range_begin - buff; *out_end = bracket_range_end - buff; - /* Update the inout_cursor_offset. Note this may cause it to exceed str.size(), though overflow is not likely */ + // Update the inout_cursor_offset. Note this may cause it to exceed str.size(), though + // overflow is not likely. *inout_cursor_offset = 1 + *out_end; } return ret; } -int parse_util_locate_cmdsubst_range(const wcstring &str, size_t *inout_cursor_offset, wcstring *out_contents, size_t *out_start, size_t *out_end, bool accept_incomplete) -{ - return parse_util_locate_brackets_range(str, inout_cursor_offset, out_contents, out_start, out_end, accept_incomplete, L'(', L')'); +int parse_util_locate_cmdsubst_range(const wcstring &str, size_t *inout_cursor_offset, + wcstring *out_contents, size_t *out_start, size_t *out_end, + bool accept_incomplete) { + return parse_util_locate_brackets_range(str, inout_cursor_offset, out_contents, out_start, + out_end, accept_incomplete, L'(', L')'); } -void parse_util_cmdsubst_extent(const wchar_t *buff, size_t cursor_pos, const wchar_t **a, const wchar_t **b) -{ - const wchar_t * const cursor = buff + cursor_pos; +void parse_util_cmdsubst_extent(const wchar_t *buff, size_t cursor_pos, const wchar_t **a, + const wchar_t **b) { + const wchar_t *const cursor = buff + cursor_pos; - CHECK(buff,); + CHECK(buff, ); const size_t bufflen = wcslen(buff); assert(cursor_pos <= bufflen); - /* ap and bp are the beginning and end of the tightest command substitition found so far */ + // ap and bp are the beginning and end of the tightest command substitition found so far. const wchar_t *ap = buff, *bp = buff + bufflen; const wchar_t *pos = buff; - for (;;) - { + for (;;) { wchar_t *begin = NULL, *end = NULL; - if (parse_util_locate_cmdsubst(pos, &begin, &end, true) <= 0) - { - /* No subshell found, all done */ + if (parse_util_locate_cmdsubst(pos, &begin, &end, true) <= 0) { + // No subshell found, all done. break; } - /* Interpret NULL to mean the end */ - if (end == NULL) - { + // Interpret NULL to mean the end. + if (end == NULL) { end = const_cast(buff) + bufflen; } - if (begin < cursor && end >= cursor) - { - /* This command substitution surrounds the cursor, so it's a tighter fit */ + if (begin < cursor && end >= cursor) { + // This command substitution surrounds the cursor, so it's a tighter fit. begin++; ap = begin; bp = end; - /* pos is where to begin looking for the next one. But if we reached the end there's no next one. */ - if (begin >= end) - break; + // pos is where to begin looking for the next one. But if we reached the end there's no + // next one. + if (begin >= end) break; pos = begin + 1; - } - else if (begin >= cursor) - { - /* This command substitution starts at or after the cursor. Since it was the first command substitution in the string, we're done. */ + } else if (begin >= cursor) { + // This command substitution starts at or after the cursor. Since it was the first + // command substitution in the string, we're done. break; - } - else - { - /* This command substitution ends before the cursor. Skip it. */ + } else { + // This command substitution ends before the cursor. Skip it. assert(end < cursor); pos = end + 1; assert(pos <= buff + bufflen); @@ -334,145 +288,86 @@ void parse_util_cmdsubst_extent(const wchar_t *buff, size_t cursor_pos, const wc if (b != NULL) *b = bp; } -/** - 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) -{ +/// 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) { const wchar_t *begin, *end; wchar_t *buffcpy; - int finished=0; + int finished = 0; - CHECK(buff,); - - if (a) - { - *a=0; - } - - if (b) - { - *b = 0; - } + CHECK(buff, ); + if (a) *a = 0; + if (b) *b = 0; parse_util_cmdsubst_extent(buff, cursor_pos, &begin, &end); - if (!end || !begin) - { + if (!end || !begin) { return; } assert(cursor_pos >= (begin - buff)); const size_t pos = cursor_pos - (begin - buff); - if (a) - { - *a = begin; - } - - if (b) - { - *b = end; - } - - buffcpy = wcsndup(begin, end-begin); - - if (!buffcpy) - { + if (a) *a = begin; + if (b) *b = end; + buffcpy = wcsndup(begin, end - begin); + if (!buffcpy) { DIE_MEM(); } tokenizer_t tok(buffcpy, TOK_ACCEPT_UNFINISHED); tok_t token; - while (tok.next(&token) && !finished) - { + while (tok.next(&token) && !finished) { size_t tok_begin = token.offset; - switch (token.type) - { - case TOK_PIPE: - { - if (!process) - { + switch (token.type) { + case TOK_PIPE: { + if (!process) { break; } } - case TOK_END: - case TOK_BACKGROUND: - { - - if (tok_begin >= pos) - { - finished=1; - if (b) - { - *b = (wchar_t *)begin + tok_begin; - } + case TOK_BACKGROUND: { + if (tok_begin >= pos) { + finished = 1; + if (b) *b = (wchar_t *)begin + tok_begin; + } else { + if (a) *a = (wchar_t *)begin + tok_begin + 1; } - else - { - if (a) - { - *a = (wchar_t *)begin + tok_begin+1; - } - } - - break; - } - - default: - { break; } + default: { break; } } } free(buffcpy); } -void parse_util_process_extent(const wchar_t *buff, - size_t pos, - const wchar_t **a, - const wchar_t **b) -{ +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_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) -{ +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 *a = NULL, *b = NULL, *pa = NULL, *pb = NULL; - CHECK(buff,); + CHECK(buff, ); assert(cursor_pos >= 0); const wchar_t *cmdsubst_begin, *cmdsubst_end; parse_util_cmdsubst_extent(buff, cursor_pos, &cmdsubst_begin, &cmdsubst_end); - if (!cmdsubst_end || !cmdsubst_begin) - { + if (!cmdsubst_end || !cmdsubst_begin) { return; } - /* pos is equivalent to cursor_pos within the range of the command substitution {begin, end} */ + // pos is equivalent to cursor_pos within the range of the command substitution {begin, end}. long offset_within_cmdsubst = cursor_pos - (cmdsubst_begin - buff); a = cmdsubst_begin + offset_within_cmdsubst; @@ -481,159 +376,103 @@ void parse_util_token_extent(const wchar_t *buff, pb = pa; assert(cmdsubst_begin >= buff); - assert(cmdsubst_begin <= (buff+wcslen(buff))); + assert(cmdsubst_begin <= (buff + wcslen(buff))); assert(cmdsubst_end >= cmdsubst_begin); - assert(cmdsubst_end <= (buff+wcslen(buff))); + assert(cmdsubst_end <= (buff + wcslen(buff))); - const wcstring buffcpy = wcstring(cmdsubst_begin, cmdsubst_end-cmdsubst_begin); + const wcstring buffcpy = wcstring(cmdsubst_begin, cmdsubst_end - cmdsubst_begin); tokenizer_t tok(buffcpy.c_str(), TOK_ACCEPT_UNFINISHED | TOK_SQUASH_ERRORS); tok_t token; - while (tok.next(&token)) - { + while (tok.next(&token)) { size_t tok_begin = token.offset; size_t tok_end = tok_begin; - /* - Calculate end of token - */ - if (token.type == TOK_STRING) - { + // Calculate end of token. + if (token.type == TOK_STRING) { tok_end += token.text.size(); } - /* - 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 > offset_within_cmdsubst) - { + // 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 > offset_within_cmdsubst) { a = b = cmdsubst_begin + offset_within_cmdsubst; break; } - /* - If cursor is inside the token, this is the token we are - looking for. If so, set a and b and break - */ - if (token.type == TOK_STRING && tok_end >= offset_within_cmdsubst) - { + // If cursor is inside the token, this is the token we are looking for. If so, set a and b + // and break. + if (token.type == TOK_STRING && tok_end >= offset_within_cmdsubst) { a = cmdsubst_begin + token.offset; b = a + token.text.size(); break; } - /* - Remember previous string token - */ - if (token.type == TOK_STRING) - { + // Remember previous string token. + if (token.type == TOK_STRING) { pa = cmdsubst_begin + token.offset; pb = pa + token.text.size(); } } - if (tok_begin) - { - *tok_begin = a; - } - - if (tok_end) - { - *tok_end = b; - } - - if (prev_begin) - { - *prev_begin = pa; - } - - if (prev_end) - { - *prev_end = pb; - } + 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(pa <= (buff + wcslen(buff))); assert(pb >= pa); - assert(pb <= (buff+wcslen(buff))); - + assert(pb <= (buff + wcslen(buff))); } -wcstring parse_util_unescape_wildcards(const wcstring &str) -{ +wcstring parse_util_unescape_wildcards(const wcstring &str) { wcstring result; result.reserve(str.size()); - - const wchar_t * const cs = str.c_str(); - for (size_t i=0; cs[i] != L'\0'; i++) - { - if (cs[i] == L'*') - { + + const wchar_t *const cs = str.c_str(); + for (size_t i = 0; cs[i] != L'\0'; i++) { + if (cs[i] == L'*') { result.push_back(ANY_STRING); - } - else if (cs[i] == L'?') - { + } else if (cs[i] == L'?') { result.push_back(ANY_CHAR); - } - else if (cs[i] == L'\\' && (cs[i+1] == L'*' || cs[i+1] == L'?')) - { - result.push_back(cs[i+1]); + } else if (cs[i] == L'\\' && (cs[i + 1] == L'*' || cs[i + 1] == L'?')) { + result.push_back(cs[i + 1]); i += 1; - } - else if (cs[i] == L'\\' && cs[i+1] == L'\\') - { - // Not a wildcard, but ensure the next iteration - // doesn't see this escaped backslash + } else if (cs[i] == L'\\' && cs[i + 1] == L'\\') { + // Not a wildcard, but ensure the next iteration doesn't see this escaped backslash. result.append(L"\\\\"); i += 1; - } - else - { + } else { result.push_back(cs[i]); } } return result; } -/** - Find the outermost quoting style of current token. Returns 0 if - token is not quoted. +/// Find the outermost quoting style of current token. Returns 0 if token is not quoted. +static wchar_t get_quote(const wcstring &cmd_str, size_t len) { + size_t i = 0; + wchar_t res = 0; + const wchar_t *const cmd = cmd_str.c_str(); -*/ -static wchar_t get_quote(const wcstring &cmd_str, size_t len) -{ - size_t i=0; - wchar_t res=0; - const wchar_t * const cmd = cmd_str.c_str(); + while (1) { + if (!cmd[i]) break; - while (1) - { - if (!cmd[i]) - break; - - if (cmd[i] == L'\\') - { + if (cmd[i] == L'\\') { i++; - if (!cmd[i]) - break; + if (!cmd[i]) break; i++; - } - else - { - if (cmd[i] == L'\'' || cmd[i] == L'\"') - { + } 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)) - { + // 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 = end - cmd + 1; + } else i++; } } @@ -641,83 +480,63 @@ static wchar_t get_quote(const wcstring &cmd_str, size_t len) return res; } -void parse_util_get_parameter_info(const wcstring &cmd, const size_t pos, wchar_t *quote, size_t *offset, enum token_type *out_type) -{ - size_t prev_pos=0; +void parse_util_get_parameter_info(const wcstring &cmd, const size_t pos, wchar_t *quote, + size_t *offset, enum token_type *out_type) { + size_t prev_pos = 0; wchar_t last_quote = '\0'; int unfinished; tokenizer_t tok(cmd.c_str(), TOK_ACCEPT_UNFINISHED | TOK_SQUASH_ERRORS); tok_t token; - while (tok.next(&token)) - { - if (token.offset > pos) - break; + while (tok.next(&token)) { + if (token.offset > pos) break; - if (token.type == TOK_STRING) - last_quote = get_quote(token.text, pos - token.offset); + if (token.type == TOK_STRING) last_quote = get_quote(token.text, pos - token.offset); - if (out_type != NULL) - *out_type = token.type; + if (out_type != NULL) *out_type = token.type; prev_pos = token.offset; } wchar_t *cmd_tmp = wcsdup(cmd.c_str()); - cmd_tmp[pos]=0; + cmd_tmp[pos] = 0; size_t cmdlen = wcslen(cmd_tmp); - unfinished = (cmdlen==0); - if (!unfinished) - { + unfinished = (cmdlen == 0); + if (!unfinished) { unfinished = (quote != 0); - if (!unfinished) - { - if (wcschr(L" \t\n\r", cmd_tmp[cmdlen-1]) != 0) - { - if ((cmdlen == 1) || (cmd_tmp[cmdlen-2] != L'\\')) - { - unfinished=1; + if (!unfinished) { + if (wcschr(L" \t\n\r", cmd_tmp[cmdlen - 1]) != 0) { + if ((cmdlen == 1) || (cmd_tmp[cmdlen - 2] != L'\\')) { + unfinished = 1; } } } } - if (quote) - *quote = last_quote; + if (quote) *quote = last_quote; - if (offset != 0) - { - if (!unfinished) - { - while ((cmd_tmp[prev_pos] != 0) && (wcschr(L";|",cmd_tmp[prev_pos])!= 0)) - prev_pos++; + if (offset != 0) { + if (!unfinished) { + while ((cmd_tmp[prev_pos] != 0) && (wcschr(L";|", cmd_tmp[prev_pos]) != 0)) prev_pos++; *offset = prev_pos; - } - else - { + } 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); - } - else - { + } else { bool unescapable = false; - for (size_t i = 0; i < cmd.size(); i++) - { + for (size_t i = 0; i < cmd.size(); i++) { wchar_t c = cmd.at(i); - switch (c) - { + switch (c) { case L'\n': case L'\t': case L'\b': @@ -725,15 +544,13 @@ wcstring parse_util_escape_string_with_quote(const wcstring &cmd, wchar_t quote) unescapable = true; break; default: - if (c == quote) - result.push_back(L'\\'); + if (c == quote) result.push_back(L'\\'); result.push_back(c); break; } } - if (unescapable) - { + if (unescapable) { result = escape_string(cmd, ESCAPE_ALL | ESCAPE_NO_QUOTED); result.insert(0, "e, 1); } @@ -741,220 +558,219 @@ wcstring parse_util_escape_string_with_quote(const wcstring &cmd, wchar_t quote) return result; } -/* We are given a parse tree, the index of a node within the tree, its indent, and a vector of indents the same size as the original source string. Set the indent correspdonding to the node's source range, if appropriate. +/// We are given a parse tree, the index of a node within the tree, its indent, and a vector of +/// indents the same size as the original source string. Set the indent correspdonding to the node's +/// source range, if appropriate. +/// +/// trailing_indent is the indent for nodes with unrealized source, i.e. if I type 'if false ' +/// then we have an if node with an empty job list (without source) but we want the last line to be +/// indented anyways. +/// +/// switch statements also indent. +/// +/// max_visited_node_idx is the largest index we visited. +static void compute_indents_recursive(const parse_node_tree_t &tree, node_offset_t node_idx, + int node_indent, parse_token_type_t parent_type, + std::vector *indents, int *trailing_indent, + node_offset_t *max_visited_node_idx) { + // Guard against incomplete trees. + if (node_idx > tree.size()) return; - trailing_indent is the indent for nodes with unrealized source, i.e. if I type 'if false ' then we have an if node with an empty job list (without source) but we want the last line to be indented anyways. - - switch statements also indent. - - max_visited_node_idx is the largest index we visited. -*/ -static void compute_indents_recursive(const parse_node_tree_t &tree, node_offset_t node_idx, int node_indent, parse_token_type_t parent_type, std::vector *indents, int *trailing_indent, node_offset_t *max_visited_node_idx) -{ - /* Guard against incomplete trees */ - if (node_idx > tree.size()) - return; - - /* Update max_visited_node_idx */ - if (node_idx > *max_visited_node_idx) - *max_visited_node_idx = node_idx; - - /* We could implement this by utilizing the fish grammar. But there's an easy trick instead: almost everything that wraps a job list should be indented by 1. So just find all of the job lists. One exception is switch, which wraps a case_item_list instead of a job_list. The other exception is job_list itself: a job_list is a job and a job_list, and we want that child list to be indented the same as the parent. So just find all job_lists whose parent is not a job_list, and increment their indent by 1. We also want to treat andor_job_list like job_lists */ + // Update max_visited_node_idx. + if (node_idx > *max_visited_node_idx) *max_visited_node_idx = node_idx; + // We could implement this by utilizing the fish grammar. But there's an easy trick instead: + // almost everything that wraps a job list should be indented by 1. So just find all of the job + // lists. One exception is switch, which wraps a case_item_list instead of a job_list. The other + // exception is job_list itself: a job_list is a job and a job_list, and we want that child list + // to be indented the same as the parent. So just find all job_lists whose parent is not a + // job_list, and increment their indent by 1. We also want to treat andor_job_list like + // job_lists. const parse_node_t &node = tree.at(node_idx); const parse_token_type_t node_type = node.type; - /* Increment the indent if we are either a root job_list, or root case_item_list */ - const bool is_root_job_list = node_type != parent_type && (node_type == symbol_job_list || node_type == symbol_andor_job_list); - const bool is_root_case_item_list = (node_type == symbol_case_item_list && parent_type != symbol_case_item_list); - if (is_root_job_list || is_root_case_item_list) - { + // Increment the indent if we are either a root job_list, or root case_item_list. + const bool is_root_job_list = node_type != parent_type && (node_type == symbol_job_list || + node_type == symbol_andor_job_list); + const bool is_root_case_item_list = + (node_type == symbol_case_item_list && parent_type != symbol_case_item_list); + if (is_root_job_list || is_root_case_item_list) { node_indent += 1; } - /* If we have source, store the trailing indent unconditionally. If we do not have source, store the trailing indent only if ours is bigger; this prevents the trailing "run" of terminal job lists from affecting the trailing indent. For example, code like this: - - if foo - - will be parsed as this: - - job_list - job - if_statement - job [if] - job_list [empty] - job_list [empty] - - There's two "terminal" job lists, and we want the innermost one. - - Note we are relying on the fact that nodes are in the same order as the source, i.e. an in-order traversal of the node tree also traverses the source from beginning to end. - */ - if (node.has_source() || node_indent > *trailing_indent) - { + // If we have source, store the trailing indent unconditionally. If we do not have source, store + // the trailing indent only if ours is bigger; this prevents the trailing "run" of terminal job + // lists from affecting the trailing indent. For example, code like this: + // + // if foo + // + // will be parsed as this: + // + // job_list + // job + // if_statement + // job [if] + // job_list [empty] + // job_list [empty] + // + // There's two "terminal" job lists, and we want the innermost one. + // + // Note we are relying on the fact that nodes are in the same order as the source, i.e. an + // in-order traversal of the node tree also traverses the source from beginning to end. + if (node.has_source() || node_indent > *trailing_indent) { *trailing_indent = node_indent; } - - /* Store the indent into the indent array */ - if (node.source_start != SOURCE_OFFSET_INVALID && node.source_start < indents->size()) - { - if (node.has_source()) - { - /* A normal non-empty node. Store the indent unconditionally. */ + // Store the indent into the indent array. + if (node.source_start != SOURCE_OFFSET_INVALID && node.source_start < indents->size()) { + if (node.has_source()) { + // A normal non-empty node. Store the indent unconditionally. indents->at(node.source_start) = node_indent; - } - else - { - /* An empty node. We have a source offset but no source length. This can come about when a node legitimately empty: - - while true; end - - The job_list inside the while loop is empty. It still has a source offset (at the end of the while statement) but no source extent. - We still need to capture that indent, because there may be comments inside: - while true - # loop forever - end - - The 'loop forever' comment must be indented, by virtue of storing the indent. - - Now consider what happens if we remove the end: - - while true - # loop forever - - Now both the job_list and end_command are unmaterialized. However, we want the indent to be of the job_list and not the end_command. Therefore, we only store the indent if it's bigger. - */ - if (node_indent > indents->at(node.source_start)) - { + } else { + // An empty node. We have a source offset but no source length. This can come about when + // a node legitimately empty: + // + // while true; end + // + // The job_list inside the while loop is empty. It still has a source offset (at the end + // of the while statement) but no source extent. We still need to capture that indent, + // because there may be comments inside: + // + // while true + // # loop forever + // end + // + // The 'loop forever' comment must be indented, by virtue of storing the indent. + // + // Now consider what happens if we remove the end: + // + // while true + // # loop forever + // + // Now both the job_list and end_command are unmaterialized. However, we want the indent + // to be of the job_list and not the end_command. Therefore, we only store the indent + // if it's bigger. + if (node_indent > indents->at(node.source_start)) { indents->at(node.source_start) = node_indent; } } } - - /* Recursive to all our children */ - for (node_offset_t idx = 0; idx < node.child_count; idx++) - { - /* Note we pass our type to our child, which becomes its parent node type */ - compute_indents_recursive(tree, node.child_start + idx, node_indent, node_type, indents, trailing_indent, max_visited_node_idx); + // Recursive to all our children. + for (node_offset_t idx = 0; idx < node.child_count; idx++) { + // Note we pass our type to our child, which becomes its parent node type. + compute_indents_recursive(tree, node.child_start + idx, node_indent, node_type, indents, + trailing_indent, max_visited_node_idx); } } -std::vector parse_util_compute_indents(const wcstring &src) -{ - /* Make a vector the same size as the input string, which contains the indents. Initialize them to -1. */ +std::vector parse_util_compute_indents(const wcstring &src) { + // Make a vector the same size as the input string, which contains the indents. Initialize them + // to -1. const size_t src_size = src.size(); std::vector indents(src_size, -1); - /* Parse the string. We pass continue_after_error to produce a forest; the trailing indent of the last node we visited becomes the input indent of the next. I.e. in the case of 'switch foo ; cas', we get an invalid parse tree (since 'cas' is not valid) but we indent it as if it were a case item list */ + // Parse the string. We pass continue_after_error to produce a forest; the trailing indent of + // the last node we visited becomes the input indent of the next. I.e. in the case of 'switch + // foo ; cas', we get an invalid parse tree (since 'cas' is not valid) but we indent it as if it + // were a case item list. parse_node_tree_t tree; - parse_tree_from_string(src, parse_flag_continue_after_error | parse_flag_include_comments | parse_flag_accept_incomplete_tokens, &tree, NULL /* errors */); + parse_tree_from_string(src, parse_flag_continue_after_error | parse_flag_include_comments | + parse_flag_accept_incomplete_tokens, + &tree, NULL /* errors */); - /* Start indenting at the first node. If we have a parse error, we'll have to start indenting from the top again */ + // Start indenting at the first node. If we have a parse error, we'll have to start indenting + // from the top again. node_offset_t start_node_idx = 0; int last_trailing_indent = 0; - while (start_node_idx < tree.size()) - { - /* The indent that we'll get for the last line */ + while (start_node_idx < tree.size()) { + // The indent that we'll get for the last line. int trailing_indent = 0; - /* Biggest offset we visited */ + // Biggest offset we visited. node_offset_t max_visited_node_idx = 0; - /* Invoke the recursive version. As a hack, pass job_list for the 'parent' token type, which will prevent the really-root job list from indenting */ - compute_indents_recursive(tree, start_node_idx, last_trailing_indent, symbol_job_list, &indents, &trailing_indent, &max_visited_node_idx); + // Invoke the recursive version. As a hack, pass job_list for the 'parent' token type, which + // will prevent the really-root job list from indenting. + compute_indents_recursive(tree, start_node_idx, last_trailing_indent, symbol_job_list, + &indents, &trailing_indent, &max_visited_node_idx); - /* We may have more to indent. The trailing indent becomes our current indent. Start at the node after the last we visited. */ + // We may have more to indent. The trailing indent becomes our current indent. Start at the + // node after the last we visited. last_trailing_indent = trailing_indent; start_node_idx = max_visited_node_idx + 1; } - /* Handle comments. Each comment node has a parent (which is whatever the top of the symbol stack was when the comment was encountered). So the source range of the comment has the same indent as its parent. */ + // Handle comments. Each comment node has a parent (which is whatever the top of the symbol + // stack was when the comment was encountered). So the source range of the comment has the same + // indent as its parent. const size_t tree_size = tree.size(); - for (node_offset_t i=0; i < tree_size; i++) - { + for (node_offset_t i = 0; i < tree_size; i++) { const parse_node_t &node = tree.at(i); - if (node.type == parse_special_type_comment && node.has_source() && node.parent < tree_size) - { + if (node.type == parse_special_type_comment && node.has_source() && + node.parent < tree_size) { const parse_node_t &parent = tree.at(node.parent); - if (parent.source_start != SOURCE_OFFSET_INVALID) - { + if (parent.source_start != SOURCE_OFFSET_INVALID) { indents.at(node.source_start) = indents.at(parent.source_start); } } } - /* Now apply the indents. The indents array has -1 for places where the indent does not change, so start at each value and extend it along the run of -1s */ + // Now apply the indents. The indents array has -1 for places where the indent does not change, + // so start at each value and extend it along the run of -1s. int last_indent = 0; - for (size_t i=0; ipush_back(error); return true; } - -/** - 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) -{ - return contains(word, - L"exec", - L"case", - L"break", - L"return", - L"continue"); +/// 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) { + return contains(word, L"exec", L"case", L"break", L"return", L"continue"); } -bool parse_util_argument_is_help(const wchar_t *s, int min_match) -{ +bool parse_util_argument_is_help(const wchar_t *s, int min_match) { CHECK(s, 0); size_t len = wcslen(s); @@ -965,14 +781,14 @@ bool parse_util_argument_is_help(const wchar_t *s, int min_match) (len >= (size_t)min_match && (wcsncmp(L"--help", s, len) == 0)); } -// Check if the first argument under the given node is --help -static bool first_argument_is_help(const parse_node_tree_t &node_tree, const parse_node_t &node, const wcstring &src) -{ +/// Check if the first argument under the given node is --help. +static bool first_argument_is_help(const parse_node_tree_t &node_tree, const parse_node_t &node, + const wcstring &src) { bool is_help = false; - const parse_node_tree_t::parse_node_list_t arg_nodes = node_tree.find_nodes(node, symbol_argument, 1); - if (! arg_nodes.empty()) - { - // Check the first argument only + const parse_node_tree_t::parse_node_list_t arg_nodes = + node_tree.find_nodes(node, symbol_argument, 1); + if (!arg_nodes.empty()) { + // Check the first argument only. const parse_node_t &arg = *arg_nodes.at(0); const wcstring first_arg_src = arg.get_source(src); is_help = parse_util_argument_is_help(first_arg_src.c_str(), 3); @@ -980,48 +796,47 @@ static bool first_argument_is_help(const parse_node_tree_t &node_tree, const par return is_help; } -/* If a var name or command is too long for error reporting, make it shorter */ -static wcstring truncate_string(const wcstring &str) -{ +/// If a var name or command is too long for error reporting, make it shorter. +static wcstring truncate_string(const wcstring &str) { const size_t max_len = 16; wcstring result(str, 0, max_len); - if (str.size() > max_len) - { + if (str.size() > max_len) { // Truncate! - if (ellipsis_char == L'\x2026') - { + if (ellipsis_char == L'\x2026') { result.at(max_len - 1) = ellipsis_char; - } - else - { + } else { result.replace(max_len - 3, 3, L"..."); } } return result; } -/* Given a wide character immediately after a dollar sign, return the appropriate error message. For example, if wc is @, then the variable name was $@ and we suggest $argv. */ -static const wchar_t *error_format_for_character(wchar_t wc) -{ - switch (wc) - { - case L'?': return ERROR_NOT_STATUS; - case L'#': return ERROR_NOT_ARGV_COUNT; - case L'@': return ERROR_NOT_ARGV_AT; - case L'*': return ERROR_NOT_ARGV_STAR; +/// Given a wide character immediately after a dollar sign, return the appropriate error message. +/// For example, if wc is @, then the variable name was $@ and we suggest $argv. +static const wchar_t *error_format_for_character(wchar_t wc) { + switch (wc) { + case L'?': + return ERROR_NOT_STATUS; + case L'#': + return ERROR_NOT_ARGV_COUNT; + case L'@': + return ERROR_NOT_ARGV_AT; + case L'*': + return ERROR_NOT_ARGV_STAR; case L'$': case VARIABLE_EXPAND: case VARIABLE_EXPAND_SINGLE: case VARIABLE_EXPAND_EMPTY: return ERROR_NOT_PID; - default: return ERROR_BAD_VAR_CHAR1; + default: + return ERROR_BAD_VAR_CHAR1; } } -void parse_util_expand_variable_error(const wcstring &token, size_t global_token_pos, size_t dollar_pos, parse_error_list_t *errors) -{ - // Note that dollar_pos is probably VARIABLE_EXPAND or VARIABLE_EXPAND_SINGLE, - // not a literal dollar sign. +void parse_util_expand_variable_error(const wcstring &token, size_t global_token_pos, + size_t dollar_pos, parse_error_list_t *errors) { + // Note that dollar_pos is probably VARIABLE_EXPAND or VARIABLE_EXPAND_SINGLE, not a literal + // dollar sign. assert(errors != NULL); assert(dollar_pos < token.size()); const bool double_quotes = (token.at(dollar_pos) == VARIABLE_EXPAND_SINGLE); @@ -1030,137 +845,112 @@ void parse_util_expand_variable_error(const wcstring &token, size_t global_token const size_t global_after_dollar_pos = global_dollar_pos + 1; wchar_t char_after_dollar = dollar_pos + 1 >= token.size() ? 0 : token.at(dollar_pos + 1); - switch (char_after_dollar) - { + switch (char_after_dollar) { case BRACKET_BEGIN: case L'{': - + { - // The BRACKET_BEGIN is for unquoted, the { is for quoted. Anyways we have (possible quoted) ${. See if we have a }, and the stuff in between is variable material. If so, report a bracket error. Otherwise just complain about the ${. + // The BRACKET_BEGIN is for unquoted, the { is for quoted. Anyways we have (possible + // quoted) ${. See if we have a }, and the stuff in between is variable material. If so, + // report a bracket error. Otherwise just complain about the ${. bool looks_like_variable = false; - size_t closing_bracket = token.find(char_after_dollar == L'{' ? L'}' : BRACKET_END, dollar_pos + 2); + size_t closing_bracket = + token.find(char_after_dollar == L'{' ? L'}' : BRACKET_END, dollar_pos + 2); wcstring var_name; - if (closing_bracket != wcstring::npos) - { + if (closing_bracket != wcstring::npos) { size_t var_start = dollar_pos + 2, var_end = closing_bracket; var_name = wcstring(token, var_start, var_end - var_start); - looks_like_variable = ! var_name.empty() && wcsvarname(var_name.c_str()) == NULL; + looks_like_variable = !var_name.empty() && wcsvarname(var_name.c_str()) == NULL; } - if (looks_like_variable) - { - append_syntax_error(errors, - global_after_dollar_pos, - double_quotes ? ERROR_BRACKETED_VARIABLE_QUOTED1 : ERROR_BRACKETED_VARIABLE1, - truncate_string(var_name).c_str()); - } - else - { - append_syntax_error(errors, - global_after_dollar_pos, - ERROR_BAD_VAR_CHAR1, - L'{'); + if (looks_like_variable) { + append_syntax_error( + errors, global_after_dollar_pos, + double_quotes ? ERROR_BRACKETED_VARIABLE_QUOTED1 : ERROR_BRACKETED_VARIABLE1, + truncate_string(var_name).c_str()); + } else { + append_syntax_error(errors, global_after_dollar_pos, ERROR_BAD_VAR_CHAR1, L'{'); } break; } - - case INTERNAL_SEPARATOR: - { - //e.g.: echo foo"$"baz - // These are only ever quotes, not command substitutions. Command substitutions are handled earlier. - append_syntax_error(errors, - global_dollar_pos, - ERROR_NO_VAR_NAME); + + case INTERNAL_SEPARATOR: { + // e.g.: echo foo"$"baz + // These are only ever quotes, not command substitutions. Command substitutions are + // handled earlier. + append_syntax_error(errors, global_dollar_pos, ERROR_NO_VAR_NAME); break; } - - case '(': - { + + case '(': { // e.g.: 'echo "foo$(bar)baz" // Try to determine what's in the parens. wcstring token_after_parens; wcstring paren_text; size_t open_parens = dollar_pos + 1, cmdsub_start = 0, cmdsub_end = 0; - if (parse_util_locate_cmdsubst_range(token, - &open_parens, - &paren_text, - &cmdsub_start, - &cmdsub_end, - true) > 0) - { + if (parse_util_locate_cmdsubst_range(token, &open_parens, &paren_text, &cmdsub_start, + &cmdsub_end, true) > 0) { token_after_parens = tok_first(paren_text); } - - /* Make sure we always show something */ - if (token_after_parens.empty()) - { + + // Make sure we always show something. + if (token_after_parens.empty()) { token_after_parens = L"..."; } - - append_syntax_error(errors, - global_dollar_pos, - ERROR_BAD_VAR_SUBCOMMAND1, + + append_syntax_error(errors, global_dollar_pos, ERROR_BAD_VAR_SUBCOMMAND1, truncate_string(token_after_parens).c_str()); break; } - - case L'\0': - { - append_syntax_error(errors, - global_dollar_pos, - ERROR_NO_VAR_NAME); + + case L'\0': { + append_syntax_error(errors, global_dollar_pos, ERROR_NO_VAR_NAME); break; } - - default: - { + + default: { wchar_t token_stop_char = char_after_dollar; - // Unescape (see #50) + // Unescape (see issue #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'*'; - - /* Determine which error message to use. The format string may not consume all the arguments we pass but that's harmless. */ + + // Determine which error message to use. The format string may not consume all the + // arguments we pass but that's harmless. const wchar_t *error_fmt = error_format_for_character(token_stop_char); - - append_syntax_error(errors, - global_after_dollar_pos, - error_fmt, - token_stop_char); + + append_syntax_error(errors, global_after_dollar_pos, error_fmt, token_stop_char); break; } - } - - // We should have appended exactly one error + + // We should have appended exactly one error. assert(errors->size() == start_error_count + 1); } -/* Detect cases like $(abc). Given an arg like foo(bar), let arg_src be foo and cmdsubst_src be bar. If arg ends with VARIABLE_EXPAND, then report an error. */ -static parser_test_error_bits_t detect_dollar_cmdsub_errors(size_t arg_src_offset, const wcstring &arg_src, const wcstring &cmdsubst_src, parse_error_list_t *out_errors) -{ +/// Detect cases like $(abc). Given an arg like foo(bar), let arg_src be foo and cmdsubst_src be +/// bar. If arg ends with VARIABLE_EXPAND, then report an error. +static parser_test_error_bits_t detect_dollar_cmdsub_errors(size_t arg_src_offset, + const wcstring &arg_src, + const wcstring &cmdsubst_src, + parse_error_list_t *out_errors) { parser_test_error_bits_t result_bits = 0; wcstring unescaped_arg_src; - if (unescape_string(arg_src, &unescaped_arg_src, UNESCAPE_SPECIAL)) - { - if (! unescaped_arg_src.empty()) - { + if (unescape_string(arg_src, &unescaped_arg_src, UNESCAPE_SPECIAL)) { + if (!unescaped_arg_src.empty()) { wchar_t last = unescaped_arg_src.at(unescaped_arg_src.size() - 1); - if (last == VARIABLE_EXPAND) - { + if (last == VARIABLE_EXPAND) { result_bits |= PARSER_TEST_ERROR; - if (out_errors != NULL) - { + if (out_errors != NULL) { wcstring subcommand_first_token = tok_first(cmdsubst_src); - if (subcommand_first_token.empty()) - { + if (subcommand_first_token.empty()) { // e.g. $(). Report somthing. subcommand_first_token = L"..."; } - append_syntax_error(out_errors, - arg_src_offset + arg_src.size() - 1, // global position of the dollar - ERROR_BAD_VAR_SUBCOMMAND1, - truncate_string(subcommand_first_token).c_str()); + append_syntax_error( + out_errors, + arg_src_offset + arg_src.size() - 1, // global position of the dollar + ERROR_BAD_VAR_SUBCOMMAND1, truncate_string(subcommand_first_token).c_str()); } } } @@ -1168,74 +958,64 @@ static parser_test_error_bits_t detect_dollar_cmdsub_errors(size_t arg_src_offse return result_bits; } -/** - Test if this argument contains any errors. Detected errors include - syntax errors in command substitutions, improperly escaped - characters and improper use of the variable expansion operator. -*/ -parser_test_error_bits_t parse_util_detect_errors_in_argument(const parse_node_t &node, const wcstring &arg_src, parse_error_list_t *out_errors) -{ +/// Test if this argument contains any errors. Detected errors include syntax errors in command +/// substitutions, improperly escaped characters and improper use of the variable expansion +/// operator. +parser_test_error_bits_t parse_util_detect_errors_in_argument(const parse_node_t &node, + const wcstring &arg_src, + parse_error_list_t *out_errors) { assert(node.type == symbol_argument); - int err=0; - + int err = 0; wchar_t *paran_begin, *paran_end; int do_loop = 1; - wcstring working_copy = arg_src; - while (do_loop) - { + while (do_loop) { const wchar_t *working_copy_cstr = working_copy.c_str(); - switch (parse_util_locate_cmdsubst(working_copy_cstr, - ¶n_begin, - ¶n_end, - false)) - { - case -1: - { - err=1; - if (out_errors) - { + switch (parse_util_locate_cmdsubst(working_copy_cstr, ¶n_begin, ¶n_end, false)) { + case -1: { + err = 1; + if (out_errors) { append_syntax_error(out_errors, node.source_start, L"Mismatched parenthesis"); } return err; } - case 0: - { + case 0: { do_loop = 0; break; } - case 1: - { - + case 1: { const wcstring subst(paran_begin + 1, paran_end); - // Replace the command substitution with just INTERNAL_SEPARATOR + // Replace the command substitution with just INTERNAL_SEPARATOR. size_t cmd_sub_start = paran_begin - working_copy_cstr; size_t cmd_sub_len = paran_end + 1 - paran_begin; working_copy.replace(cmd_sub_start, cmd_sub_len, wcstring(1, INTERNAL_SEPARATOR)); parse_error_list_t subst_errors; - err |= parse_util_detect_errors(subst, &subst_errors, false /* do not accept incomplete */); + err |= parse_util_detect_errors(subst, &subst_errors, + false /* do not accept incomplete */); - /* Our command substitution produced error offsets relative to its source. Tweak the offsets of the errors in the command substitution to account for both its offset within the string, and the offset of the node */ + // Our command substitution produced error offsets relative to its source. Tweak the + // offsets of the errors in the command substitution to account for both its offset + // within the string, and the offset of the node. size_t error_offset = cmd_sub_start + 1 + node.source_start; parse_error_offset_source_start(&subst_errors, error_offset); - - if (out_errors != NULL) - { + + if (out_errors != NULL) { out_errors->insert(out_errors->end(), subst_errors.begin(), subst_errors.end()); - - /* Hackish. Take this opportunity to report $(...) errors. We do this because after we've replaced with internal separators, we can't distinguish between "" and (), and also we no longer have the source of the command substitution. As an optimization, this is only necessary if the last character is a $. */ - if (cmd_sub_start > 0 && working_copy.at(cmd_sub_start - 1) == L'$') - { + + // Hackish. Take this opportunity to report $(...) errors. We do this because + // after we've replaced with internal separators, we can't distinguish between + // "" and (), and also we no longer have the source of the command substitution. + // As an optimization, this is only necessary if the last character is a $. + if (cmd_sub_start > 0 && working_copy.at(cmd_sub_start - 1) == L'$') { err |= detect_dollar_cmdsub_errors(node.source_start, working_copy.substr(0, cmd_sub_start), - subst, - out_errors); + subst, out_errors); } } break; @@ -1244,39 +1024,34 @@ parser_test_error_bits_t parse_util_detect_errors_in_argument(const parse_node_t } wcstring unesc; - if (! unescape_string(working_copy, &unesc, UNESCAPE_SPECIAL)) - { - if (out_errors) - { - append_syntax_error(out_errors, node.source_start, L"Invalid token '%ls'", working_copy.c_str()); + if (!unescape_string(working_copy, &unesc, UNESCAPE_SPECIAL)) { + if (out_errors) { + append_syntax_error(out_errors, node.source_start, L"Invalid token '%ls'", + working_copy.c_str()); } return 1; - } - else - { - /* Check for invalid variable expansions */ + } else { + // Check for invalid variable expansions. const size_t unesc_size = unesc.size(); - for (size_t idx = 0; idx < unesc_size; idx++) - { - switch (unesc.at(idx)) - { + for (size_t idx = 0; idx < unesc_size; idx++) { + switch (unesc.at(idx)) { case VARIABLE_EXPAND: - case VARIABLE_EXPAND_SINGLE: - { + case VARIABLE_EXPAND_SINGLE: { wchar_t next_char = (idx + 1 < unesc_size ? unesc.at(idx + 1) : L'\0'); - if (next_char != VARIABLE_EXPAND && next_char != VARIABLE_EXPAND_SINGLE && ! wcsvarchr(next_char)) - { - err=1; - if (out_errors) - { - /* We have something like $$$^.... Back up until we reach the first $ */ + if (next_char != VARIABLE_EXPAND && next_char != VARIABLE_EXPAND_SINGLE && + !wcsvarchr(next_char)) { + err = 1; + if (out_errors) { + // We have something like $$$^.... Back up until we reach the first $. size_t first_dollar = idx; - while (first_dollar > 0 && (unesc.at(first_dollar-1) == VARIABLE_EXPAND || unesc.at(first_dollar-1) == VARIABLE_EXPAND_SINGLE)) - { + while (first_dollar > 0 && + (unesc.at(first_dollar - 1) == VARIABLE_EXPAND || + unesc.at(first_dollar - 1) == VARIABLE_EXPAND_SINGLE)) { first_dollar--; } - parse_util_expand_variable_error(unesc, node.source_start, first_dollar, out_errors); + parse_util_expand_variable_error(unesc, node.source_start, first_dollar, + out_errors); } } @@ -1289,136 +1064,130 @@ parser_test_error_bits_t parse_util_detect_errors_in_argument(const parse_node_t return err; } -parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, parse_error_list_t *out_errors, bool allow_incomplete, parse_node_tree_t *out_tree) -{ +parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, + parse_error_list_t *out_errors, + bool allow_incomplete, + parse_node_tree_t *out_tree) { parse_node_tree_t node_tree; parse_error_list_t parse_errors; parser_test_error_bits_t res = 0; - // Whether we encountered a parse error + // Whether we encountered a parse error. bool errored = false; - // Whether we encountered an unclosed block - // We detect this via an 'end_command' block without source + // Whether we encountered an unclosed block. We detect this via an 'end_command' block without + // source. bool has_unclosed_block = false; - // Whether there's an unclosed quote, and therefore unfinished - // This is only set if allow_incomplete is set + // Whether there's an unclosed quote, and therefore unfinished. This is only set if + // allow_incomplete is set. bool has_unclosed_quote = false; - // Parse the input string into a parse tree - // Some errors are detected here - bool parsed = parse_tree_from_string(buff_src, allow_incomplete ? parse_flag_leave_unterminated : parse_flag_none, &node_tree, &parse_errors); + // Parse the input string into a parse tree. Some errors are detected here. + bool parsed = parse_tree_from_string( + buff_src, allow_incomplete ? parse_flag_leave_unterminated : parse_flag_none, &node_tree, + &parse_errors); - if (allow_incomplete) - { - for (size_t i=0; i < parse_errors.size(); i++) - { - if (parse_errors.at(i).code == parse_error_tokenizer_unterminated_quote) - { - // Remove this error, since we don't consider it a real error + if (allow_incomplete) { + for (size_t i = 0; i < parse_errors.size(); i++) { + if (parse_errors.at(i).code == parse_error_tokenizer_unterminated_quote) { + // Remove this error, since we don't consider it a real error. has_unclosed_quote = true; parse_errors.erase(parse_errors.begin() + i); i--; } } } - - // #1238: If the only error was unterminated quote, then consider this to have parsed successfully. A better fix would be to have parse_tree_from_string return this information directly (but it would be a shame to munge up its nice bool return). - if (parse_errors.empty() && has_unclosed_quote) - { + + // Issue #1238: If the only error was unterminated quote, then consider this to have parsed + // successfully. A better fix would be to have parse_tree_from_string return this information + // directly (but it would be a shame to munge up its nice bool return). + if (parse_errors.empty() && has_unclosed_quote) { parsed = true; } - if (! parsed) - { + if (!parsed) { errored = true; } - // has_unclosed_quote may only be set if allow_incomplete is true - assert(! has_unclosed_quote || allow_incomplete); - - // Expand all commands - // Verify 'or' and 'and' not used inside pipelines - // Verify pipes via parser_is_pipe_forbidden - // Verify return only within a function - // Verify no variable expansions + // has_unclosed_quote may only be set if allow_incomplete is true. + assert(!has_unclosed_quote || allow_incomplete); - if (! errored) - { + // Expand all commands. + // Verify 'or' and 'and' not used inside pipelines. + // Verify pipes via parser_is_pipe_forbidden. + // Verify return only within a function. + // Verify no variable expansions. + + if (!errored) { const size_t node_tree_size = node_tree.size(); - for (size_t i=0; i < node_tree_size; i++) - { + for (size_t i = 0; i < node_tree_size; i++) { const parse_node_t &node = node_tree.at(i); - if (node.type == symbol_end_command && ! node.has_source()) - { - // an 'end' without source is an unclosed block + if (node.type == symbol_end_command && !node.has_source()) { + // An 'end' without source is an unclosed block. has_unclosed_block = true; - } - else if (node.type == symbol_boolean_statement) - { - // 'or' and 'and' can be in a pipeline, as long as they're first + } else if (node.type == symbol_boolean_statement) { + // 'or' and 'and' can be in a pipeline, as long as they're first. parse_bool_statement_type_t type = parse_node_tree_t::statement_boolean_type(node); - if ((type == parse_bool_and || type == parse_bool_or) && node_tree.statement_is_in_pipeline(node, false /* don't count first */)) - { - errored = append_syntax_error(&parse_errors, node.source_start, EXEC_ERR_MSG, (type == parse_bool_and) ? L"and" : L"or"); + if ((type == parse_bool_and || type == parse_bool_or) && + node_tree.statement_is_in_pipeline(node, false /* don't count first */)) { + errored = append_syntax_error(&parse_errors, node.source_start, EXEC_ERR_MSG, + (type == parse_bool_and) ? L"and" : L"or"); } - } - else if (node.type == symbol_argument) - { + } else if (node.type == symbol_argument) { const wcstring arg_src = node.get_source(buff_src); res |= parse_util_detect_errors_in_argument(node, arg_src, &parse_errors); - } - else if (node.type == symbol_job) - { - if (node_tree.job_should_be_backgrounded(node)) - { - /* Disallow background in the following cases: - - foo & ; and bar - foo & ; or bar - if foo & ; end - while foo & ; end - */ + } else if (node.type == symbol_job) { + if (node_tree.job_should_be_backgrounded(node)) { + // Disallow background in the following cases: + // + // foo & ; and bar + // foo & ; or bar + // if foo & ; end + // while foo & ; end const parse_node_t *job_parent = node_tree.get_parent(node); assert(job_parent != NULL); - switch (job_parent->type) - { + switch (job_parent->type) { case symbol_if_clause: - case symbol_while_header: - { + case symbol_while_header: { assert(node_tree.get_child(*job_parent, 1) == &node); - errored = append_syntax_error(&parse_errors, node.source_start, BACKGROUND_IN_CONDITIONAL_ERROR_MSG); + errored = append_syntax_error(&parse_errors, node.source_start, + BACKGROUND_IN_CONDITIONAL_ERROR_MSG); break; } - - case symbol_job_list: - { - // This isn't very complete, e.g. we don't catch 'foo & ; not and bar' + + case symbol_job_list: { + // This isn't very complete, e.g. we don't catch 'foo & ; not and bar'. assert(node_tree.get_child(*job_parent, 0) == &node); - const parse_node_t *next_job_list = node_tree.get_child(*job_parent, 1, symbol_job_list); + const parse_node_t *next_job_list = + node_tree.get_child(*job_parent, 1, symbol_job_list); assert(next_job_list != NULL); - const parse_node_t *next_job = node_tree.next_node_in_node_list(*next_job_list, symbol_job, NULL); - if (next_job != NULL) - { - const parse_node_t *next_statement = node_tree.get_child(*next_job, 0, symbol_statement); - if (next_statement != NULL) - { - const parse_node_t *spec_statement = node_tree.get_child(*next_statement, 0); - if (spec_statement && spec_statement->type == symbol_boolean_statement) - { - switch (parse_node_tree_t::statement_boolean_type(*spec_statement)) - { - // These are not allowed + const parse_node_t *next_job = + node_tree.next_node_in_node_list(*next_job_list, symbol_job, NULL); + if (next_job != NULL) { + const parse_node_t *next_statement = + node_tree.get_child(*next_job, 0, symbol_statement); + if (next_statement != NULL) { + const parse_node_t *spec_statement = + node_tree.get_child(*next_statement, 0); + if (spec_statement && + spec_statement->type == symbol_boolean_statement) { + switch (parse_node_tree_t::statement_boolean_type( + *spec_statement)) { + // These are not allowed. case parse_bool_and: - errored = append_syntax_error(&parse_errors, spec_statement->source_start, BOOL_AFTER_BACKGROUND_ERROR_MSG, L"and"); + errored = append_syntax_error( + &parse_errors, spec_statement->source_start, + BOOL_AFTER_BACKGROUND_ERROR_MSG, L"and"); break; case parse_bool_or: - errored = append_syntax_error(&parse_errors, spec_statement->source_start, BOOL_AFTER_BACKGROUND_ERROR_MSG, L"or"); + errored = append_syntax_error( + &parse_errors, spec_statement->source_start, + BOOL_AFTER_BACKGROUND_ERROR_MSG, L"or"); break; case parse_bool_not: - // This one is OK + // This one is OK. break; } } @@ -1426,138 +1195,131 @@ parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, pars } break; } - + default: - break; + break; } } - } - else if (node.type == symbol_plain_statement) - { - // In a few places below, we want to know if we are in a pipeline - const bool is_in_pipeline = node_tree.statement_is_in_pipeline(node, true /* count first */); + } else if (node.type == symbol_plain_statement) { + // In a few places below, we want to know if we are in a pipeline. + const bool is_in_pipeline = + node_tree.statement_is_in_pipeline(node, true /* count first */); - // We need to know the decoration - const enum parse_statement_decoration_t decoration = node_tree.decoration_for_plain_statement(node); + // We need to know the decoration. + const enum parse_statement_decoration_t decoration = + node_tree.decoration_for_plain_statement(node); - // Check that we don't try to pipe through exec - if (is_in_pipeline && decoration == parse_statement_decoration_exec) - { - errored = append_syntax_error(&parse_errors, node.source_start, EXEC_ERR_MSG, L"exec"); + // Check that we don't try to pipe through exec. + if (is_in_pipeline && decoration == parse_statement_decoration_exec) { + errored = append_syntax_error(&parse_errors, node.source_start, EXEC_ERR_MSG, + L"exec"); } wcstring command; - if (node_tree.command_for_plain_statement(node, buff_src, &command)) - { - // Check that we can expand the command - if (! expand_one(command, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES | EXPAND_SKIP_JOBS, NULL)) - { - // TODO: leverage the resulting errors - errored = append_syntax_error(&parse_errors, node.source_start, ILLEGAL_CMD_ERR_MSG, command.c_str()); + if (node_tree.command_for_plain_statement(node, buff_src, &command)) { + // Check that we can expand the command. + if (!expand_one(command, + EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES | EXPAND_SKIP_JOBS, + NULL)) { + // TODO: leverage the resulting errors. + errored = append_syntax_error(&parse_errors, node.source_start, + ILLEGAL_CMD_ERR_MSG, command.c_str()); } - // Check that pipes are sound - if (! errored && parser_is_pipe_forbidden(command) && is_in_pipeline) - { - errored = append_syntax_error(&parse_errors, node.source_start, EXEC_ERR_MSG, command.c_str()); + // Check that pipes are sound. + if (!errored && parser_is_pipe_forbidden(command) && is_in_pipeline) { + errored = append_syntax_error(&parse_errors, node.source_start, + EXEC_ERR_MSG, command.c_str()); } - // Check that we don't return from outside a function - // But we allow it if it's 'return --help' - if (! errored && command == L"return") - { + // Check that we don't return from outside a function. But we allow it if it's + // 'return --help'. + if (!errored && command == L"return") { const parse_node_t *ancestor = &node; bool found_function = false; - while (ancestor != NULL) - { - const parse_node_t *possible_function_header = node_tree.header_node_for_block_statement(*ancestor); - if (possible_function_header != NULL && possible_function_header->type == symbol_function_header) - { + while (ancestor != NULL) { + const parse_node_t *possible_function_header = + node_tree.header_node_for_block_statement(*ancestor); + if (possible_function_header != NULL && + possible_function_header->type == symbol_function_header) { found_function = true; break; } ancestor = node_tree.get_parent(*ancestor); - } - if (! found_function && ! first_argument_is_help(node_tree, node, buff_src)) - { - errored = append_syntax_error(&parse_errors, node.source_start, INVALID_RETURN_ERR_MSG); + if (!found_function && !first_argument_is_help(node_tree, node, buff_src)) { + errored = append_syntax_error(&parse_errors, node.source_start, + INVALID_RETURN_ERR_MSG); } } - // Check that we don't break or continue from outside a loop - if (! errored && (command == L"break" || command == L"continue")) - { - // Walk up until we hit a 'for' or 'while' loop. If we hit a function first, stop the search; we can't break an outer loop from inside a function. - // This is a little funny because we can't tell if it's a 'for' or 'while' loop from the ancestor alone; we need the header. That is, we hit a block_statement, and have to check its header. + // Check that we don't break or continue from outside a loop. + if (!errored && (command == L"break" || command == L"continue")) { + // Walk up until we hit a 'for' or 'while' loop. If we hit a function first, + // stop the search; we can't break an outer loop from inside a function. + // This is a little funny because we can't tell if it's a 'for' or 'while' + // loop from the ancestor alone; we need the header. That is, we hit a + // block_statement, and have to check its header. bool found_loop = false, end_search = false; const parse_node_t *ancestor = &node; - while (ancestor != NULL && ! end_search) - { - const parse_node_t *loop_or_function_header = node_tree.header_node_for_block_statement(*ancestor); - if (loop_or_function_header != NULL) - { - switch (loop_or_function_header->type) - { + while (ancestor != NULL && !end_search) { + const parse_node_t *loop_or_function_header = + node_tree.header_node_for_block_statement(*ancestor); + if (loop_or_function_header != NULL) { + switch (loop_or_function_header->type) { case symbol_while_header: case symbol_for_header: - // this is a loop header, so we can break or continue + // This is a loop header, so we can break or continue. found_loop = true; end_search = true; break; case symbol_function_header: - // this is a function header, so we cannot break or continue. We stop our search here. + // This is a function header, so we cannot break or + // continue. We stop our search here. found_loop = false; end_search = true; break; default: - // most likely begin / end style block, which makes no difference + // Most likely begin / end style block, which makes no + // difference. break; } } ancestor = node_tree.get_parent(*ancestor); } - if (! found_loop && ! first_argument_is_help(node_tree, node, buff_src)) - { - errored = append_syntax_error(&parse_errors, - node.source_start, - (command == L"break" ? INVALID_BREAK_ERR_MSG : INVALID_CONTINUE_ERR_MSG)); + if (!found_loop && !first_argument_is_help(node_tree, node, buff_src)) { + errored = append_syntax_error( + &parse_errors, node.source_start, + (command == L"break" ? INVALID_BREAK_ERR_MSG + : INVALID_CONTINUE_ERR_MSG)); } } - // Check that we don't do an invalid builtin (#1252) - if (! errored && decoration == parse_statement_decoration_builtin && ! builtin_exists(command)) - { - errored = append_syntax_error(&parse_errors, - node.source_start, - UNKNOWN_BUILTIN_ERR_MSG, - command.c_str()); + // Check that we don't do an invalid builtin (issue #1252). + if (!errored && decoration == parse_statement_decoration_builtin && + !builtin_exists(command)) { + errored = append_syntax_error(&parse_errors, node.source_start, + UNKNOWN_BUILTIN_ERR_MSG, command.c_str()); } - } } } } - if (errored) - res |= PARSER_TEST_ERROR; + if (errored) res |= PARSER_TEST_ERROR; - if (has_unclosed_block || has_unclosed_quote) - res |= PARSER_TEST_INCOMPLETE; + if (has_unclosed_block || has_unclosed_quote) res |= PARSER_TEST_INCOMPLETE; - if (out_errors != NULL) - { + if (out_errors != NULL) { out_errors->swap(parse_errors); } - - if (out_tree != NULL) - { + + if (out_tree != NULL) { out_tree->swap(node_tree); } return res; - } diff --git a/src/parse_util.h b/src/parse_util.h index 454ac8061..92a1e939a 100644 --- a/src/parse_util.h +++ b/src/parse_util.h @@ -1,191 +1,152 @@ -/** \file parse_util.h - - Various mostly unrelated utility functions related to parsing, - loading and evaluating fish code. -*/ +// Various mostly unrelated utility functions related to parsing, loading and evaluating fish code. #ifndef FISH_PARSE_UTIL_H #define FISH_PARSE_UTIL_H +#include #include #include -#include #include "common.h" #include "parse_constants.h" #include "tokenizer.h" -/** - Find the beginning and end of the first subshell in the specified string. - - \param in the string to search for subshells - \param begin the starting paranthesis of the subshell - \param end the ending paranthesis of the subshell - \param accept_incomplete whether to permit missing closing parenthesis - \return -1 on syntax error, 0 if no subshells exist and 1 on success -*/ - -int parse_util_locate_cmdsubst(const wchar_t *in, - wchar_t **begin, - wchar_t **end, +/// Find the beginning and end of the first subshell in the specified string. +/// +/// \param in the string to search for subshells +/// \param begin the starting paranthesis of the subshell +/// \param end the ending paranthesis of the subshell +/// \param accept_incomplete whether to permit missing closing parenthesis +/// \return -1 on syntax error, 0 if no subshells exist and 1 on success +int parse_util_locate_cmdsubst(const wchar_t *in, wchar_t **begin, wchar_t **end, bool accept_incomplete); -/** Same as parse_util_locate_cmdsubst, but handles square brackets [ ] */ -int parse_util_locate_slice(const wchar_t *in, - wchar_t **begin, - wchar_t **end, +/// Same as parse_util_locate_cmdsubst, but handles square brackets [ ]. +int parse_util_locate_slice(const wchar_t *in, wchar_t **begin, wchar_t **end, bool accept_incomplete); -/** - Alternative API. Iterate over command substitutions. - - \param str the string to search for subshells - \param inout_cursor_offset On input, the location to begin the search. On output, either the end of the string, or just after the closed-paren. - \param out_contents On output, the contents of the command substitution - \param out_start On output, the offset of the start of the command substitution (open paren) - \param out_end On output, the offset of the end of the command substitution (close paren), or the end of the string if it was incomplete - \param accept_incomplete whether to permit missing closing parenthesis - \return -1 on syntax error, 0 if no subshells exist and 1 on success -*/ - -int parse_util_locate_cmdsubst_range(const wcstring &str, - size_t *inout_cursor_offset, - wcstring *out_contents, - size_t *out_start, - size_t *out_end, +/// Alternative API. Iterate over command substitutions. +/// +/// \param str the string to search for subshells +/// \param inout_cursor_offset On input, the location to begin the search. On output, either the end +/// of the string, or just after the closed-paren. +/// \param out_contents On output, the contents of the command substitution +/// \param out_start On output, the offset of the start of the command substitution (open paren) +/// \param out_end On output, the offset of the end of the command substitution (close paren), or +/// the end of the string if it was incomplete +/// \param accept_incomplete whether to permit missing closing parenthesis +/// \return -1 on syntax error, 0 if no subshells exist and 1 on success +int parse_util_locate_cmdsubst_range(const wcstring &str, size_t *inout_cursor_offset, + wcstring *out_contents, size_t *out_start, size_t *out_end, bool accept_incomplete); -/** - Find the beginning and end of the command substitution under the - cursor. If no subshell is found, the entire string is returned. If - the current command substitution is not ended, i.e. the closing - parenthesis is missing, then the string from the beginning of the - substitution to the end of the string is returned. - - \param buff the string to search for subshells - \param cursor_pos the position of the cursor - \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, +/// Find the beginning and end of the command substitution under the cursor. If no subshell is +/// found, the entire string is returned. If the current command substitution is not ended, i.e. the +/// closing parenthesis is missing, then the string from the beginning of the substitution to the +/// end of the string is returned. +/// +/// \param buff the string to search for subshells +/// \param cursor_pos the position of the cursor +/// \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); -/** - Find the beginning and end of the process definition under the cursor - - \param buff the string to search for subshells - \param cursor_pos the position of the cursor - \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, +/// Find the beginning and end of the process definition under the cursor +/// +/// \param buff the string to search for subshells +/// \param cursor_pos the position of the cursor +/// \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); - -/** - Find the beginning and end of the job definition under the cursor - - \param buff the string to search for subshells - \param cursor_pos the position of the cursor - \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, +/// Find the beginning and end of the job definition under the cursor +/// +/// \param buff the string to search for subshells +/// \param cursor_pos the position of the cursor +/// \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); -/** - Find the beginning and end of the token under the cursor and the - token before the current token. Any combination of tok_begin, - tok_end, prev_begin and prev_end may be null. - - \param buff the string to search for subshells - \param cursor_pos the position of the cursor - \param tok_begin the start of the current token - \param tok_end the end of the current token - \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, +/// Find the beginning and end of the token under the cursor and the token before the current token. +/// Any combination of tok_begin, tok_end, prev_begin and prev_end may be null. +/// +/// \param buff the string to search for subshells +/// \param cursor_pos the position of the cursor +/// \param tok_begin the start of the current token +/// \param tok_end the end of the current token +/// \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); - -/** - Get the linenumber at the specified character offset -*/ +/// Get the linenumber at the specified character offset. int parse_util_lineno(const wchar_t *str, size_t len); -/** - Calculate the line number of the specified cursor position - */ +/// Calculate the line number of the specified cursor position. int parse_util_get_line_from_offset(const wcstring &str, size_t pos); -/** - Get the offset of the first character on the specified line - */ +/// Get the offset of the first character on the specified 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 - */ +/// 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); -/** - Return the given string, unescaping wildcard characters but not performing - any other character transformation. -*/ +/// Return the given string, unescaping wildcard characters but not performing any other character +/// transformation. wcstring parse_util_unescape_wildcards(const wcstring &in); -/** - Checks if the specified string is a help option. - - \param s the string to test - \param min_match is the minimum number of characters that must match in a long style option, i.e. the longest common prefix between --help and any other option. If less than 3, 3 will be assumed. -*/ +/// Checks if the specified string is a help option. +/// +/// \param s the string to test +/// \param min_match is the minimum number of characters that must match in a long style option, +/// i.e. the longest common prefix between --help and any other option. If less than 3, 3 will be +/// assumed. bool parse_util_argument_is_help(const wchar_t *s, int min_match); +/// Calculates information on the parameter at the specified index. +/// +/// \param cmd The command to be analyzed +/// \param pos An index in the string which is inside the parameter +/// \param quote If not NULL, store the type of quote this parameter has, can be either ', " or \\0, +/// meaning the string is not quoted. +/// \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. +void parse_util_get_parameter_info(const wcstring &cmd, const size_t pos, wchar_t *quote, + size_t *offset, enum token_type *out_type); -/** - Calculates information on the parameter at the specified index. - - \param cmd The command to be analyzed - \param pos An index in the string which is inside the parameter - \param quote If not NULL, store the type of quote this parameter has, can be either ', " or \\0, meaning the string is not quoted. - \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. -*/ -void parse_util_get_parameter_info(const wcstring &cmd, const size_t pos, wchar_t *quote, size_t *offset, enum token_type *out_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). -*/ +/// 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); -/** Given a string, parse it as fish code and then return the indents. The return value has the same size as the string */ +/// Given a string, parse it as fish code and then return the indents. The return value has the same +/// size as the string. std::vector parse_util_compute_indents(const wcstring &src); -/** Given a string, detect parse errors in it. If allow_incomplete is set, then if the string is incomplete (e.g. an unclosed quote), an error is not returned and the PARSER_TEST_INCOMPLETE bit is set in the return value. If allow_incomplete is not set, then incomplete strings result in an error. If out_tree is not NULL, the resulting tree is returned by reference. */ +/// Given a string, detect parse errors in it. If allow_incomplete is set, then if the string is +/// incomplete (e.g. an unclosed quote), an error is not returned and the PARSER_TEST_INCOMPLETE bit +/// is set in the return value. If allow_incomplete is not set, then incomplete strings result in an +/// error. If out_tree is not NULL, the resulting tree is returned by reference. class parse_node_tree_t; -parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, parse_error_list_t *out_errors = NULL, bool allow_incomplete = true, parse_node_tree_t *out_tree = NULL); +parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, + parse_error_list_t *out_errors = NULL, + bool allow_incomplete = true, + parse_node_tree_t *out_tree = NULL); -/** - Test if this argument contains any errors. Detected errors include syntax errors in command substitutions, improperly escaped characters and improper use of the variable expansion operator. - - This does NOT currently detect unterminated quotes. -*/ +/// Test if this argument contains any errors. Detected errors include syntax errors in command +/// substitutions, improperly escaped characters and improper use of the variable expansion +/// operator. This does NOT currently detect unterminated quotes. class parse_node_t; -parser_test_error_bits_t parse_util_detect_errors_in_argument(const parse_node_t &node, const wcstring &arg_src, parse_error_list_t *out_errors = NULL); +parser_test_error_bits_t parse_util_detect_errors_in_argument( + const parse_node_t &node, const wcstring &arg_src, parse_error_list_t *out_errors = NULL); -/* Given a string containing a variable expansion error, append an appropriate error to the errors list. The global_token_pos is the offset of the token in the larger source, and the dollar_pos is the offset of the offending dollar sign within the token. */ -void parse_util_expand_variable_error(const wcstring &token, size_t global_token_pos, size_t dollar_pos, parse_error_list_t *out_errors); +/// Given a string containing a variable expansion error, append an appropriate error to the errors +/// list. The global_token_pos is the offset of the token in the larger source, and the dollar_pos +/// is the offset of the offending dollar sign within the token. +void parse_util_expand_variable_error(const wcstring &token, size_t global_token_pos, + size_t dollar_pos, parse_error_list_t *out_errors); #endif From 80250c0729d382aa60b4c6ff6210b66b470ad40b Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Mon, 2 May 2016 17:22:44 -0700 Subject: [PATCH 202/363] restyle parser module to match project style Reduces lint errors from 72 to 44 (-43%). Line count from 1698 to 1313 (-23%). Another step in resolving issue #2902. --- src/parse_constants.h | 45 -- src/parser.cpp | 1025 +++++++++++++++------------------------ src/parser.h | 448 +++++++---------- src/parser_keywords.cpp | 58 +-- src/parser_keywords.h | 46 +- 5 files changed, 596 insertions(+), 1026 deletions(-) diff --git a/src/parse_constants.h b/src/parse_constants.h index c43ba7de9..01580d151 100644 --- a/src/parse_constants.h +++ b/src/parse_constants.h @@ -268,49 +268,4 @@ void parse_error_offset_source_start(parse_error_list_t *errors, size_t amt); #define ERROR_BAD_COMMAND_ASSIGN_ERR_MSG \ _(L"Unsupported use of '='. In fish, please use 'set %ls %ls'.") -/// While block description. -#define WHILE_BLOCK N_(L"'while' block") - -/// For block description. -#define FOR_BLOCK N_(L"'for' block") - -/// Breakpoint block. -#define BREAKPOINT_BLOCK N_(L"Block created by breakpoint") - -/// If block description. -#define IF_BLOCK N_(L"'if' conditional block") - -/// Function definition block description. -#define FUNCTION_DEF_BLOCK N_(L"function definition block") - -/// Function invocation block description. -#define FUNCTION_CALL_BLOCK N_(L"function invocation block") - -/// Function invocation block description. -#define FUNCTION_CALL_NO_SHADOW_BLOCK N_(L"function invocation block with no variable shadowing") - -/// Switch block description. -#define SWITCH_BLOCK N_(L"'switch' block") - -/// Fake block description. -#define FAKE_BLOCK N_(L"unexecutable block") - -/// Top block description. -#define TOP_BLOCK N_(L"global root block") - -/// Command substitution block description. -#define SUBST_BLOCK N_(L"command substitution block") - -/// Begin block description. -#define BEGIN_BLOCK N_(L"'begin' unconditional block") - -/// Source block description. -#define SOURCE_BLOCK N_(L"Block created by the . builtin") - -/// Source block description. -#define EVENT_BLOCK N_(L"event handler block") - -/// Unknown block description. -#define UNKNOWN_BLOCK N_(L"unknown/invalid block") - #endif diff --git a/src/parser.cpp b/src/parser.cpp index 9202d7fd0..ff10ea8ed 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1,274 +1,190 @@ -/** \file parser.c - -The fish parser. Contains functions for parsing and evaluating code. -*/ +// The fish parser. Contains functions for parsing and evaluating code. +#include +#include #include #include -#include #include #include -#include -#include "fallback.h" // IWYU pragma: keep #include "common.h" -#include "wutil.h" // IWYU pragma: keep -#include "proc.h" -#include "parser.h" -#include "function.h" #include "env.h" +#include "event.h" #include "expand.h" +#include "fallback.h" // IWYU pragma: keep +#include "function.h" +#include "intern.h" +#include "parse_constants.h" +#include "parse_execution.h" +#include "parse_tree.h" +#include "parse_util.h" +#include "parser.h" +#include "proc.h" #include "reader.h" #include "sanity.h" -#include "event.h" -#include "intern.h" -#include "parse_util.h" -#include "parse_tree.h" -#include "parse_execution.h" -#include "parse_constants.h" +#include "wutil.h" // IWYU pragma: keep class io_chain_t; -/** - Error for evaluating in illegal scope -*/ -#define INVALID_SCOPE_ERR_MSG _( L"Tried to evaluate commands using invalid block type '%ls'" ) +/// Error for evaluating in illegal scope. +#define INVALID_SCOPE_ERR_MSG _(L"Tried to evaluate commands using invalid block type '%ls'") -/** - While block description -*/ -#define WHILE_BLOCK N_( L"'while' block" ) +/// While block description. +#define WHILE_BLOCK N_(L"'while' block") -/** - For block description -*/ -#define FOR_BLOCK N_( L"'for' block" ) +/// For block description. +#define FOR_BLOCK N_(L"'for' block") -/** - Breakpoint block -*/ -#define BREAKPOINT_BLOCK N_( L"Block created by breakpoint" ) +/// Breakpoint block. +#define BREAKPOINT_BLOCK N_(L"Block created by breakpoint") -/** - If block description -*/ -#define IF_BLOCK N_( L"'if' conditional block" ) +/// If block description. +#define IF_BLOCK N_(L"'if' conditional block") -/** - Function definition block description -*/ -#define FUNCTION_DEF_BLOCK N_( L"function definition block" ) +/// Function definition block description. +#define FUNCTION_DEF_BLOCK N_(L"function definition block") -/** - Function invocation block description -*/ -#define FUNCTION_CALL_BLOCK N_( L"function invocation block" ) +/// Function invocation block description. +#define FUNCTION_CALL_BLOCK N_(L"function invocation block") -/** - Function invocation block description -*/ -#define FUNCTION_CALL_NO_SHADOW_BLOCK N_( L"function invocation block with no variable shadowing" ) +/// Function invocation block description. +#define FUNCTION_CALL_NO_SHADOW_BLOCK N_(L"function invocation block with no variable shadowing") -/** - Switch block description -*/ -#define SWITCH_BLOCK N_( L"'switch' block" ) +/// Switch block description. +#define SWITCH_BLOCK N_(L"'switch' block") -/** - Fake block description -*/ -#define FAKE_BLOCK N_( L"unexecutable block" ) +/// Fake block description. +#define FAKE_BLOCK N_(L"unexecutable block") -/** - Top block description -*/ -#define TOP_BLOCK N_( L"global root block" ) +/// Top block description. +#define TOP_BLOCK N_(L"global root block") -/** - Command substitution block description -*/ -#define SUBST_BLOCK N_( L"command substitution block" ) +/// Command substitution block description. +#define SUBST_BLOCK N_(L"command substitution block") -/** - Begin block description -*/ -#define BEGIN_BLOCK N_( L"'begin' unconditional block" ) +/// Begin block description. +#define BEGIN_BLOCK N_(L"'begin' unconditional block") -/** - Source block description -*/ -#define SOURCE_BLOCK N_( L"Block created by the . builtin" ) +/// Source block description. +#define SOURCE_BLOCK N_(L"Block created by the . builtin") -/** - Source block description -*/ -#define EVENT_BLOCK N_( L"event handler block" ) +/// Source block description. +#define EVENT_BLOCK N_(L"event handler block") -/** - Unknown block description -*/ -#define UNKNOWN_BLOCK N_( L"unknown/invalid block" ) +/// Unknown block description. +#define UNKNOWN_BLOCK N_(L"unknown/invalid block") - -/** - Datastructure to describe a block type, like while blocks, command substitution blocks, etc. -*/ -struct block_lookup_entry -{ - - /** - The block type id. The legal values are defined in parser.h. - */ +/// Datastructure to describe a block type, like while blocks, command substitution blocks, etc. +struct block_lookup_entry { + // 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. - */ + // The name of the builtin that creates this type of block, if any. const wchar_t *name; - - /** - A description of this block type - */ + // 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 } }; +/// 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}}; + // Given a file path, return something nicer. Currently we just "unexpand" tildes. -static wcstring user_presentable_path(const wcstring &path) -{ +static wcstring user_presentable_path(const wcstring &path) { return replace_home_directory_with_tilde(path); } +parser_t::parser_t() : cancellation_requested(false), is_within_fish_initialization(false) {} -parser_t::parser_t() : - cancellation_requested(false), - is_within_fish_initialization(false) -{ -} - -/* A pointer to the principal parser (which is a static local) */ +/// A pointer to the principal parser (which is a static local). static parser_t *s_principal_parser = NULL; -parser_t &parser_t::principal_parser(void) -{ +parser_t &parser_t::principal_parser(void) { ASSERT_IS_NOT_FORKED_CHILD(); ASSERT_IS_MAIN_THREAD(); static parser_t parser; - if (! s_principal_parser) - { + if (!s_principal_parser) { s_principal_parser = &parser; } return parser; } -void parser_t::set_is_within_fish_initialization(bool flag) -{ +void parser_t::set_is_within_fish_initialization(bool flag) { is_within_fish_initialization = flag; } -void parser_t::skip_all_blocks(void) -{ - /* Tell all blocks to skip */ - if (s_principal_parser) - { +void parser_t::skip_all_blocks(void) { + // Tell all blocks to skip. + if (s_principal_parser) { s_principal_parser->cancellation_requested = true; - //write(2, "Cancelling blocks\n", strlen("Cancelling blocks\n")); - for (size_t i=0; i < s_principal_parser->block_count(); i++) - { + // write(2, "Cancelling blocks\n", strlen("Cancelling blocks\n")); + for (size_t i = 0; i < s_principal_parser->block_count(); i++) { s_principal_parser->block_at_index(i)->skip = true; } } } -void parser_t::push_block(block_t *new_current) -{ +void parser_t::push_block(block_t *new_current) { const enum block_type_t type = new_current->type(); new_current->src_lineno = parser_t::get_lineno(); const wchar_t *filename = parser_t::current_filename(); - if (filename != NULL) - { + if (filename != NULL) { new_current->src_filename = intern(filename); } const block_t *old_current = this->current_block(); - if (old_current && old_current->skip) - { + if (old_current && old_current->skip) { new_current->skip = true; } - /* - 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... :-( - */ + // 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... :-( new_current->skip = old_current ? old_current->skip : 0; - /* - Type TOP and SUBST are never skipped - */ - if (type == TOP || type == SUBST) - { + // Type TOP and SUBST are never skipped. + if (type == TOP || type == SUBST) { new_current->skip = 0; } - /* - Fake blocks and function definition blocks are never executed - */ - if (type == FAKE || type == FUNCTION_DEF) - { + // Fake blocks and function definition blocks are never executed. + if (type == FAKE || type == FUNCTION_DEF) { new_current->skip = 1; } new_current->job = 0; - new_current->loop_status=LOOP_NORMAL; + new_current->loop_status = LOOP_NORMAL; this->block_stack.push_back(new_current); - // Types TOP and SUBST are not considered blocks for the purposes of `status -b` - if (type != TOP && type != SUBST) - { + // Types TOP and SUBST are not considered blocks for the purposes of `status -b`. + if (type != TOP && type != SUBST) { is_block = 1; } - if ((new_current->type() != FUNCTION_DEF) && - (new_current->type() != FAKE) && - (new_current->type() != TOP)) - { + if ((new_current->type() != FUNCTION_DEF) && (new_current->type() != FAKE) && + (new_current->type() != TOP)) { env_push(type == FUNCTION_CALL); new_current->wants_pop_env = true; } } -void parser_t::pop_block() -{ - if (block_stack.empty()) - { - debug(1, - L"function %s called on empty block stack.", - __func__); +void parser_t::pop_block() { + if (block_stack.empty()) { + debug(1, L"function %s called on empty block stack.", __func__); bugreport(); return; } @@ -276,18 +192,16 @@ void parser_t::pop_block() block_t *old = block_stack.back(); block_stack.pop_back(); - if (old->wants_pop_env) - env_pop(); + if (old->wants_pop_env) env_pop(); delete old; - // Figure out if `status -b` should consider us to be in a block now - int new_is_block=0; - for (std::vector::const_iterator it = block_stack.begin(), end = block_stack.end(); it != end; ++it) - { + // Figure out if `status -b` should consider us to be in a block now. + int new_is_block = 0; + for (std::vector::const_iterator it = block_stack.begin(), end = block_stack.end(); + it != end; ++it) { const enum block_type_t type = (*it)->type(); - if (type != TOP && type != SUBST) - { + if (type != TOP && type != SUBST) { new_is_block = 1; break; } @@ -295,37 +209,29 @@ void parser_t::pop_block() is_block = new_is_block; } -void parser_t::pop_block(const block_t *expected) -{ +void parser_t::pop_block(const block_t *expected) { assert(expected == this->current_block()); this->pop_block(); } -const wchar_t *parser_t::get_block_desc(int block) const -{ - for (size_t i=0; block_lookup[i].desc; i++) - { - if (block_lookup[i].type == block) - { +const wchar_t *parser_t::get_block_desc(int block) const { + for (size_t i = 0; block_lookup[i].desc; i++) { + if (block_lookup[i].type == block) { return _(block_lookup[i].desc); } } return _(UNKNOWN_BLOCK); } -wcstring parser_t::block_stack_description() const -{ +wcstring parser_t::block_stack_description() const { wcstring result; size_t idx = this->block_count(); size_t spaces = 0; - while (idx--) - { - if (spaces > 0) - { + while (idx--) { + if (spaces > 0) { result.push_back(L'\n'); } - for (size_t j=0; j < spaces; j++) - { + for (size_t j = 0; j < spaces; j++) { result.push_back(L' '); } result.append(this->block_at_index(idx)->description()); @@ -334,71 +240,49 @@ wcstring parser_t::block_stack_description() const return result; } -const block_t *parser_t::block_at_index(size_t idx) const -{ - /* 0 corresponds to the last element in our vector */ +const block_t *parser_t::block_at_index(size_t idx) const { + // Zero corresponds to the last element in our vector. size_t count = block_stack.size(); return idx < count ? block_stack.at(count - idx - 1) : NULL; } -block_t *parser_t::block_at_index(size_t idx) -{ +block_t *parser_t::block_at_index(size_t idx) { size_t count = block_stack.size(); return idx < count ? block_stack.at(count - idx - 1) : NULL; } -const block_t *parser_t::current_block() const -{ +const block_t *parser_t::current_block() const { return block_stack.empty() ? NULL : block_stack.back(); } -block_t *parser_t::current_block() -{ - return block_stack.empty() ? NULL : block_stack.back(); -} +block_t *parser_t::current_block() { return block_stack.empty() ? NULL : block_stack.back(); } -void parser_t::forbid_function(const wcstring &function) -{ - forbidden_function.push_back(function); -} +void parser_t::forbid_function(const wcstring &function) { forbidden_function.push_back(function); } -void parser_t::allow_function() -{ - forbidden_function.pop_back(); -} +void parser_t::allow_function() { forbidden_function.pop_back(); } -/** - Print profiling information to the specified stream -*/ -static void print_profile(const std::vector &items, - FILE *out) -{ - for (size_t pos = 0; pos < items.size(); pos++) - { +/// Print profiling information to the specified stream. +static void print_profile(const std::vector &items, FILE *out) { + for (size_t pos = 0; pos < items.size(); pos++) { const profile_item_t *me, *prev; size_t i; int my_time; me = items.at(pos); - if (!me->skipped) - { - my_time=me->parse+me->exec; + if (!me->skipped) { + my_time = me->parse + me->exec; - for (i=pos+1; iskipped) - { + if (prev->skipped) { continue; } - if (prev->level <= me->level) - { + if (prev->level <= me->level) { break; } - if (prev->level > me->level+1) - { + if (prev->level > me->level + 1) { continue; } @@ -406,199 +290,149 @@ static void print_profile(const std::vector &items, my_time -= prev->exec; } - if (me->cmd.size() > 0) - { - if (fwprintf(out, L"%d\t%d\t", my_time, me->parse+me->exec) < 0) - { + 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) - { + for (i = 0; i < me->level; i++) { + if (fwprintf(out, L"-") < 0) { wperror(L"fwprintf"); return; } - } - if (fwprintf(out, L"> %ls\n", me->cmd.c_str()) < 0) - { + if (fwprintf(out, L"> %ls\n", me->cmd.c_str()) < 0) { wperror(L"fwprintf"); return; } - } } } } -void parser_t::emit_profiling(const char *path) const -{ - /* Save profiling information. OK to not use CLO_EXEC here because this is called while fish is dying (and hence will not fork) */ +void parser_t::emit_profiling(const char *path) const { + // 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(path, "w"); - if (!f) - { - debug(1, - _(L"Could not write profiling information to file '%s'"), - path); - } - else - { - if (fwprintf(f, - _(L"Time\tSum\tCommand\n"), - profile_items.size()) < 0) - { + if (!f) { + debug(1, _(L"Could not write profiling information to file '%s'"), path); + } else { + if (fwprintf(f, _(L"Time\tSum\tCommand\n"), profile_items.size()) < 0) { wperror(L"fwprintf"); - } - else - { + } else { print_profile(profile_items, f); } - if (fclose(f)) - { + if (fclose(f)) { wperror(L"fclose"); } } } -void parser_t::expand_argument_list(const wcstring &arg_list_src, expand_flags_t eflags, std::vector *output_arg_list) -{ +void parser_t::expand_argument_list(const wcstring &arg_list_src, expand_flags_t eflags, + std::vector *output_arg_list) { assert(output_arg_list != NULL); - /* Parse the string as an argument list */ + // Parse the string as an argument list. parse_node_tree_t tree; - if (! parse_tree_from_string(arg_list_src, parse_flag_none, &tree, NULL /* errors */, symbol_freestanding_argument_list)) - { - /* Failed to parse. Here we expect to have reported any errors in test_args */ + if (!parse_tree_from_string(arg_list_src, parse_flag_none, &tree, NULL /* errors */, + symbol_freestanding_argument_list)) { + // Failed to parse. Here we expect to have reported any errors in test_args. return; } - /* Get the root argument list */ - assert(! tree.empty()); + // Get the root argument list. + assert(!tree.empty()); const parse_node_t *arg_list = &tree.at(0); assert(arg_list->type == symbol_freestanding_argument_list); - /* Extract arguments from it */ - while (arg_list != NULL) - { - const parse_node_t *arg_node = tree.next_node_in_node_list(*arg_list, symbol_argument, &arg_list); - if (arg_node != NULL) - { + // Extract arguments from it. + while (arg_list != NULL) { + const parse_node_t *arg_node = + tree.next_node_in_node_list(*arg_list, symbol_argument, &arg_list); + if (arg_node != NULL) { const wcstring arg_src = arg_node->get_source(arg_list_src); - if (expand_string(arg_src, output_arg_list, eflags, NULL) == EXPAND_ERROR) - { - /* Failed to expand a string */ - break; + if (expand_string(arg_src, output_arg_list, eflags, NULL) == EXPAND_ERROR) { + break; // failed to expand a string } } } } -wcstring parser_t::stack_trace() const -{ +wcstring parser_t::stack_trace() const { wcstring trace; this->stack_trace_internal(0, &trace); return trace; } -void parser_t::stack_trace_internal(size_t block_idx, wcstring *buff) const -{ - /* - Check if we should end the recursion - */ - if (block_idx >= this->block_count()) - return; +void parser_t::stack_trace_internal(size_t block_idx, wcstring *buff) const { + // Check if we should end the recursion. + if (block_idx >= this->block_count()) return; const block_t *b = this->block_at_index(block_idx); - if (b->type()==EVENT) - { - /* - This is an event handler - */ + if (b->type() == EVENT) { + // This is an event handler. const event_block_t *eb = static_cast(b); wcstring description = event_get_desc(eb->event); append_format(*buff, _(L"in event handler: %ls\n"), description.c_str()); buff->append(L"\n"); - /* - Stop recursing at event handler. No reason to believe 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. - */ + // Stop recursing at event handler. No reason to believe 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() == FUNCTION_CALL_NO_SHADOW || b->type()==SOURCE || b->type()==SUBST) - { - /* - These types of blocks should be printed - */ - + if (b->type() == FUNCTION_CALL || b->type() == FUNCTION_CALL_NO_SHADOW || 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); + 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"from sourcing file %ls\n"), user_presentable_path(source_dest).c_str()); + append_format(*buff, _(L"from sourcing file %ls\n"), + user_presentable_path(source_dest).c_str()); break; } case FUNCTION_CALL: - case FUNCTION_CALL_NO_SHADOW: - { - const function_block_t *fb = static_cast(b); + case FUNCTION_CALL_NO_SHADOW: { + const function_block_t *fb = static_cast(b); append_format(*buff, _(L"in function '%ls'\n"), fb->name.c_str()); break; } - case SUBST: - { + case SUBST: { append_format(*buff, _(L"in command substitution\n")); break; } - - default: /* Can't get here */ - break; + default: { + break; // can't get here + } } const wchar_t *file = b->src_filename; - if (file) - { - append_format(*buff, - _(L"\tcalled on line %d of file %ls\n"), - b->src_lineno, + if (file) { + append_format(*buff, _(L"\tcalled on line %d of file %ls\n"), b->src_lineno, user_presentable_path(file).c_str()); - } - else if (is_within_fish_initialization) - { + } else if (is_within_fish_initialization) { append_format(*buff, _(L"\tcalled during startup\n")); - } - else - { + } else { append_format(*buff, _(L"\tcalled on standard input\n")); } - if (b->type() == FUNCTION_CALL) - { + if (b->type() == FUNCTION_CALL) { const function_block_t *fb = static_cast(b); - const process_t * const process = fb->process; - if (process->argv(1)) - { + const process_t *const process = fb->process; + if (process->argv(1)) { wcstring tmp; - for (i=1; process->argv(i); i++) - { - if (i > 1) - tmp.push_back(L' '); + 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()); @@ -608,100 +442,77 @@ void parser_t::stack_trace_internal(size_t block_idx, wcstring *buff) const append_format(*buff, L"\n"); } - /* - Recursively print the next block - */ + // Recursively print the next block. parser_t::stack_trace_internal(block_idx + 1, buff); } -/** - Returns the name of the currently evaluated function if we are - currently evaluating a function, null otherwise. This is tested by - moving down the block-scope-stack, checking every block if it is of - type FUNCTION_CALL. -*/ -const wchar_t *parser_t::is_function() const -{ - // PCA: Have to make this a string somehow +/// Returns the name of the currently evaluated function if we are currently evaluating a function, +/// null otherwise. This is tested by moving down the block-scope-stack, checking every block if it +/// is of type FUNCTION_CALL. +const wchar_t *parser_t::is_function() const { + // PCA: Have to make this a string somehow. ASSERT_IS_MAIN_THREAD(); const wchar_t *result = NULL; - for (size_t block_idx = 0; block_idx < this->block_count(); block_idx++) - { + for (size_t block_idx = 0; block_idx < this->block_count(); block_idx++) { const block_t *b = this->block_at_index(block_idx); - if (b->type() == FUNCTION_CALL || b->type() == FUNCTION_CALL_NO_SHADOW) - { + if (b->type() == FUNCTION_CALL || b->type() == FUNCTION_CALL_NO_SHADOW) { const function_block_t *fb = static_cast(b); result = fb->name.c_str(); break; - } - else if (b->type() == SOURCE) - { - /* If a function sources a file, obviously that function's offset doesn't contribute */ + } else if (b->type() == SOURCE) { + // If a function sources a file, obviously that function's offset doesn't contribute. break; } } return result; } - -int parser_t::get_lineno() const -{ +int parser_t::get_lineno() const { int lineno = -1; - if (! execution_contexts.empty()) - { + if (!execution_contexts.empty()) { lineno = execution_contexts.back()->get_current_line_number(); - /* If we are executing a function, we have to add in its offset */ + // If we are executing a function, we have to add in its offset. const wchar_t *function_name = is_function(); - if (function_name != NULL) - { + if (function_name != NULL) { lineno += function_get_definition_offset(function_name); } - } return lineno; } -const wchar_t *parser_t::current_filename() const -{ +const wchar_t *parser_t::current_filename() const { ASSERT_IS_MAIN_THREAD(); - for (size_t i=0; i < this->block_count(); i++) - { + for (size_t i = 0; i < this->block_count(); i++) { const block_t *b = this->block_at_index(i); - if (b->type() == FUNCTION_CALL || b->type() == FUNCTION_CALL_NO_SHADOW) - { + if (b->type() == FUNCTION_CALL || b->type() == FUNCTION_CALL_NO_SHADOW) { const function_block_t *fb = static_cast(b); return function_get_definition_file(fb->name); - } - else if (b->type() == SOURCE) - { + } else if (b->type() == SOURCE) { const source_block_t *sb = static_cast(b); return sb->source_file; } } - /* We query a global array for the current file name, but only do that if we are the principal parser */ - if (this == &principal_parser()) - { + // We query a global array for the current file name, but only do that if we are the principal + // parser. + if (this == &principal_parser()) { return reader_current_filename(); } return NULL; } -wcstring parser_t::current_line() -{ - if (execution_contexts.empty()) - { +wcstring parser_t::current_line() { + if (execution_contexts.empty()) { return wcstring(); } const parse_execution_context_t *context = execution_contexts.back(); assert(context != NULL); int source_offset = context->get_current_source_offset(); - if (source_offset < 0) - { + if (source_offset < 0) { return wcstring(); } @@ -710,34 +521,29 @@ wcstring parser_t::current_line() wcstring prefix; - /* If we are not going to print a stack trace, at least print the line number and filename */ - if (!get_is_interactive() || is_function()) - { - if (file) - { - append_format(prefix, _(L"%ls (line %d): "), user_presentable_path(file).c_str(), lineno); - } - else if (is_within_fish_initialization) - { + // If we are not going to print a stack trace, at least print the line number and filename. + if (!get_is_interactive() || is_function()) { + if (file) { + append_format(prefix, _(L"%ls (line %d): "), user_presentable_path(file).c_str(), + lineno); + } else if (is_within_fish_initialization) { append_format(prefix, L"%ls: ", _(L"Startup"), lineno); - } - else - { + } else { append_format(prefix, L"%ls: ", _(L"Standard input"), lineno); } } bool is_interactive = get_is_interactive(); - bool skip_caret = is_interactive && ! is_function(); + bool skip_caret = is_interactive && !is_function(); - /* Use an error with empty text */ + // Use an error with empty text. assert(source_offset >= 0); parse_error_t empty_error = {}; empty_error.source_start = source_offset; - wcstring line_info = empty_error.describe_with_prefix(context->get_source(), prefix, is_interactive, skip_caret); - if (! line_info.empty()) - { + wcstring line_info = + empty_error.describe_with_prefix(context->get_source(), prefix, is_interactive, skip_caret); + if (!line_info.empty()) { line_info.push_back(L'\n'); } @@ -745,170 +551,150 @@ wcstring parser_t::current_line() return line_info; } -void parser_t::job_add(job_t *job) -{ +void parser_t::job_add(job_t *job) { assert(job != NULL); assert(job->first_process != NULL); this->my_job_list.push_front(job); } -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 - { + } else { debug(1, _(L"Job inconsistency")); sanity_lose(); return false; } } -void parser_t::job_promote(job_t *job) -{ +void parser_t::job_promote(job_t *job) { job_list_t::iterator loc = std::find(my_job_list.begin(), my_job_list.end(), job); assert(loc != my_job_list.end()); - /* Move the job to the beginning */ + // Move the job to the beginning. my_job_list.splice(my_job_list.begin(), my_job_list, loc); } -job_t *parser_t::job_get(job_id_t id) -{ +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) - return job; + while ((job = jobs.next())) { + if (id <= 0 || job->job_id == id) return job; } 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; + while ((job = jobs.next())) { + if (job->pgid == pid) return job; } return 0; } -profile_item_t *parser_t::create_profile_item() -{ +profile_item_t *parser_t::create_profile_item() { profile_item_t *result = NULL; - if (g_profiling_active) - { + if (g_profiling_active) { result = new profile_item_t(); profile_items.push_back(result); } return result; } -int parser_t::eval(const wcstring &cmd, const io_chain_t &io, enum block_type_t block_type) -{ - /* Parse the source into a tree, if we can */ +int parser_t::eval(const wcstring &cmd, const io_chain_t &io, enum block_type_t block_type) { + // Parse the source into a tree, if we can. parse_node_tree_t tree; parse_error_list_t error_list; - if (! parse_tree_from_string(cmd, parse_flag_none, &tree, &error_list)) - { - /* Get a backtrace. This includes the message. */ + if (!parse_tree_from_string(cmd, parse_flag_none, &tree, &error_list)) { + // Get a backtrace. This includes the message. wcstring backtrace_and_desc; this->get_backtrace(cmd, error_list, &backtrace_and_desc); - - /* Print it */ + + // Print it. fprintf(stderr, "%ls", backtrace_and_desc.c_str()); - + return 1; } return this->eval_acquiring_tree(cmd, io, block_type, moved_ref(tree)); } -int parser_t::eval_acquiring_tree(const wcstring &cmd, const io_chain_t &io, enum block_type_t block_type, moved_ref tree) -{ +int parser_t::eval_acquiring_tree(const wcstring &cmd, const io_chain_t &io, + enum block_type_t block_type, moved_ref tree) { CHECK_BLOCK(1); assert(block_type == TOP || block_type == SUBST); - if (tree.val.empty()) - { + if (tree.val.empty()) { return 0; } - /* Determine the initial eval level. If this is the first context, it's -1; otherwise it's the eval level of the top context. This is sort of wonky because we're stitching together a global notion of eval level from these separate objects. A better approach would be some profile object that all contexts share, and that tracks the eval levels on its own. */ - int exec_eval_level = (execution_contexts.empty() ? -1 : execution_contexts.back()->current_eval_level()); - - /* Append to the execution context stack */ - parse_execution_context_t *ctx = new parse_execution_context_t(tree, cmd, this, exec_eval_level); + // Determine the initial eval level. If this is the first context, it's -1; otherwise it's the + // eval level of the top context. This is sort of wonky because we're stitching together a + // global notion of eval level from these separate objects. A better approach would be some + // profile object that all contexts share, and that tracks the eval levels on its own. + int exec_eval_level = + (execution_contexts.empty() ? -1 : execution_contexts.back()->current_eval_level()); + + // Append to the execution context stack. + parse_execution_context_t *ctx = + new parse_execution_context_t(tree, cmd, this, exec_eval_level); execution_contexts.push_back(ctx); - - /* Execute the first node */ + + // Execute the first node. this->eval_block_node(0, io, block_type); - - /* Clean up the execution context stack */ - assert(! execution_contexts.empty() && execution_contexts.back() == ctx); + + // Clean up the execution context stack. + assert(!execution_contexts.empty() && execution_contexts.back() == ctx); execution_contexts.pop_back(); delete ctx; - + return 0; } -int parser_t::eval_block_node(node_offset_t node_idx, const io_chain_t &io, enum block_type_t block_type) -{ - /* Paranoia. It's a little frightening that we're given only a node_idx and we interpret this in the topmost execution context's tree. What happens if two trees were to be interleaved? Fortunately that cannot happen (yet); in the future we probably want some sort of reference counted trees. - */ +int parser_t::eval_block_node(node_offset_t node_idx, const io_chain_t &io, + enum block_type_t block_type) { + // Paranoia. It's a little frightening that we're given only a node_idx and we interpret this in + // the topmost execution context's tree. What happens if two trees were to be interleaved? + // Fortunately that cannot happen (yet); in the future we probably want some sort of reference + // counted trees. parse_execution_context_t *ctx = execution_contexts.back(); assert(ctx != NULL); CHECK_BLOCK(1); - /* Handle cancellation requests. If our block stack is currently empty, then we already did successfully cancel (or there was nothing to cancel); clear the flag. If our block stack is not empty, we are still in the process of cancelling; refuse to evaluate anything */ - if (this->cancellation_requested) - { - if (! block_stack.empty()) - { + // Handle cancellation requests. If our block stack is currently empty, then we already did + // successfully cancel (or there was nothing to cancel); clear the flag. If our block stack is + // not empty, we are still in the process of cancelling; refuse to evaluate anything. + if (this->cancellation_requested) { + if (!block_stack.empty()) { return 1; - } - else - { + } else { this->cancellation_requested = false; } } - /* Only certain blocks are allowed */ - if ((block_type != TOP) && - (block_type != SUBST)) - { - debug(1, - INVALID_SCOPE_ERR_MSG, - parser_t::get_block_desc(block_type)); + // Only certain blocks are allowed. + if ((block_type != TOP) && (block_type != SUBST)) { + debug(1, INVALID_SCOPE_ERR_MSG, parser_t::get_block_desc(block_type)); bugreport(); return 1; } - /* Not sure why we reap jobs here */ - job_reap(0); + job_reap(0); // not sure why we reap jobs here /* Start it up */ - const block_t * const start_current_block = current_block(); + const block_t *const start_current_block = current_block(); block_t *scope_block = new scope_block_t(block_type); this->push_block(scope_block); int result = ctx->eval_node_at_offset(node_idx, scope_block, io); - /* Clean up the block stack */ + // Clean up the block stack. this->pop_block(); - while (start_current_block != current_block()) - { - if (current_block() == NULL) - { - debug(0, - _(L"End of block mismatch. Program terminating.")); + while (start_current_block != current_block()) { + if (current_block() == NULL) { + debug(0, _(L"End of block mismatch. Program terminating.")); bugreport(); FATAL_EXIT(); break; @@ -916,104 +702,93 @@ int parser_t::eval_block_node(node_offset_t node_idx, const io_chain_t &io, enum this->pop_block(); } - /* Reap again */ - job_reap(0); + job_reap(0); // reap again return result; } -bool parser_t::detect_errors_in_argument_list(const wcstring &arg_list_src, wcstring *out, const wchar_t *prefix) -{ +bool parser_t::detect_errors_in_argument_list(const wcstring &arg_list_src, wcstring *out, + const wchar_t *prefix) { bool errored = false; parse_error_list_t errors; - /* Use empty string for the prefix if it's NULL */ - if (prefix == NULL) - { + // Use empty string for the prefix if it's NULL. + if (prefix == NULL) { prefix = L""; } - /* Parse the string as an argument list */ + // Parse the string as an argument list. parse_node_tree_t tree; - if (! parse_tree_from_string(arg_list_src, parse_flag_none, &tree, &errors, symbol_freestanding_argument_list)) - { - /* Failed to parse. */ + if (!parse_tree_from_string(arg_list_src, parse_flag_none, &tree, &errors, + symbol_freestanding_argument_list)) { + // Failed to parse. errored = true; } - if (! errored) - { - /* Get the root argument list */ - assert(! tree.empty()); + if (!errored) { + // Get the root argument list. + assert(!tree.empty()); const parse_node_t *arg_list = &tree.at(0); assert(arg_list->type == symbol_freestanding_argument_list); - /* Extract arguments from it */ - while (arg_list != NULL && ! errored) - { - const parse_node_t *arg_node = tree.next_node_in_node_list(*arg_list, symbol_argument, &arg_list); - if (arg_node != NULL) - { + // Extract arguments from it. + while (arg_list != NULL && !errored) { + const parse_node_t *arg_node = + tree.next_node_in_node_list(*arg_list, symbol_argument, &arg_list); + if (arg_node != NULL) { const wcstring arg_src = arg_node->get_source(arg_list_src); - if (parse_util_detect_errors_in_argument(*arg_node, arg_src, &errors)) - { + if (parse_util_detect_errors_in_argument(*arg_node, arg_src, &errors)) { errored = true; } } } } - if (! errors.empty() && out != NULL) - { - out->assign(errors.at(0).describe_with_prefix(arg_list_src, prefix, false /* not interactive */, false /* don't skip caret */)); + if (!errors.empty() && out != NULL) { + out->assign(errors.at(0).describe_with_prefix( + arg_list_src, prefix, false /* not interactive */, false /* don't skip caret */)); } return errored; } -void parser_t::get_backtrace(const wcstring &src, const parse_error_list_t &errors, wcstring *output) const -{ +void parser_t::get_backtrace(const wcstring &src, const parse_error_list_t &errors, + wcstring *output) const { assert(output != NULL); - if (! errors.empty()) - { + if (!errors.empty()) { const parse_error_t &err = errors.at(0); const bool is_interactive = get_is_interactive(); - // Determine if we want to try to print a caret to point at the source error - // The err.source_start <= src.size() check is due to the nasty way that slices work, - // which is by rewriting the source (!) + // Determine if we want to try to print a caret to point at the source error. The + // err.source_start <= src.size() check is due to the nasty way that slices work, which is + // by rewriting the source. size_t which_line = 0; bool skip_caret = true; - if (err.source_start != SOURCE_LOCATION_UNKNOWN && err.source_start <= src.size()) - { - // Determine which line we're on + if (err.source_start != SOURCE_LOCATION_UNKNOWN && err.source_start <= src.size()) { + // Determine which line we're on. which_line = 1 + std::count(src.begin(), src.begin() + err.source_start, L'\n'); - // Don't include the caret if we're interactive, this is the first line of text, and our source is at its beginning, because then it's obvious + // Don't include the caret if we're interactive, this is the first line of text, and our + // source is at its beginning, because then it's obvious. skip_caret = (is_interactive && which_line == 1 && err.source_start == 0); } wcstring prefix; const wchar_t *filename = this->current_filename(); - if (filename) - { - if (which_line > 0) - { - prefix = format_string(_(L"%ls (line %lu): "), user_presentable_path(filename).c_str(), which_line); - } - else - { + if (filename) { + if (which_line > 0) { + prefix = format_string(_(L"%ls (line %lu): "), + user_presentable_path(filename).c_str(), which_line); + } else { prefix = format_string(_(L"%ls: "), user_presentable_path(filename).c_str()); } - } - else - { + } else { prefix = L"fish: "; } - const wcstring description = err.describe_with_prefix(src, prefix, is_interactive, skip_caret); - if (! description.empty()) - { + const wcstring description = + err.describe_with_prefix(src, prefix, is_interactive, skip_caret); + if (!description.empty()) { output->append(description); output->push_back(L'\n'); } @@ -1021,147 +796,115 @@ void parser_t::get_backtrace(const wcstring &src, const parse_error_list_t &erro } } -block_t::block_t(block_type_t t) : - block_type(t), - skip(), - tok_pos(), - node_offset(NODE_OFFSET_INVALID), - loop_status(LOOP_NORMAL), - job(), - src_filename(), - src_lineno(), - wants_pop_env(false), - event_blocks() -{ -} +block_t::block_t(block_type_t t) + : block_type(t), + skip(), + tok_pos(), + node_offset(NODE_OFFSET_INVALID), + loop_status(LOOP_NORMAL), + job(), + src_filename(), + src_lineno(), + wants_pop_env(false), + event_blocks() {} -block_t::~block_t() -{ -} +block_t::~block_t() {} -wcstring block_t::description() const -{ +wcstring block_t::description() const { wcstring result; - switch (this->type()) - { - case WHILE: + switch (this->type()) { + case WHILE: { result.append(L"while"); break; - - case FOR: + } + case FOR: { result.append(L"for"); break; - - case IF: + } + case IF: { result.append(L"if"); break; - - case FUNCTION_DEF: + } + case FUNCTION_DEF: { result.append(L"function_def"); break; - - case FUNCTION_CALL: + } + case FUNCTION_CALL: { result.append(L"function_call"); break; - - case FUNCTION_CALL_NO_SHADOW: + } + case FUNCTION_CALL_NO_SHADOW: { result.append(L"function_call_no_shadow"); break; - - case SWITCH: + } + case SWITCH: { result.append(L"switch"); break; - - case FAKE: + } + case FAKE: { result.append(L"fake"); break; - - case SUBST: + } + case SUBST: { result.append(L"substitution"); break; - - case TOP: + } + case TOP: { result.append(L"top"); break; - - case BEGIN: + } + case BEGIN: { result.append(L"begin"); break; - - case SOURCE: + } + case SOURCE: { result.append(L"source"); break; - - case EVENT: + } + case EVENT: { result.append(L"event"); break; - - case BREAKPOINT: + } + case BREAKPOINT: { result.append(L"breakpoint"); break; - - default: + } + default: { append_format(result, L"unknown type %ld", (long)this->type()); break; + } } - if (this->src_lineno >= 0) - { + if (this->src_lineno >= 0) { append_format(result, L" (line %d)", this->src_lineno); } - if (this->src_filename != NULL) - { + if (this->src_filename != NULL) { append_format(result, L" (file %ls)", this->src_filename); } return result; } -/* Various block constructors */ +// Various block constructors. -if_block_t::if_block_t() : block_t(IF) -{ -} +if_block_t::if_block_t() : block_t(IF) {} -event_block_t::event_block_t(const event_t &evt) : - block_t(EVENT), - event(evt) -{ -} +event_block_t::event_block_t(const event_t &evt) : block_t(EVENT), event(evt) {} -function_block_t::function_block_t(const process_t *p, const wcstring &n, bool shadows) : - block_t(shadows ? FUNCTION_CALL : FUNCTION_CALL_NO_SHADOW), - process(p), - name(n) -{ -} +function_block_t::function_block_t(const process_t *p, const wcstring &n, bool shadows) + : block_t(shadows ? FUNCTION_CALL : FUNCTION_CALL_NO_SHADOW), process(p), name(n) {} -source_block_t::source_block_t(const wchar_t *src) : - block_t(SOURCE), - source_file(src) -{ -} +source_block_t::source_block_t(const wchar_t *src) : block_t(SOURCE), source_file(src) {} -for_block_t::for_block_t() : block_t(FOR) -{ -} +for_block_t::for_block_t() : block_t(FOR) {} -while_block_t::while_block_t() : block_t(WHILE) -{ -} +while_block_t::while_block_t() : block_t(WHILE) {} -switch_block_t::switch_block_t() : block_t(SWITCH) -{ -} +switch_block_t::switch_block_t() : block_t(SWITCH) {} -fake_block_t::fake_block_t() : block_t(FAKE) -{ -} +fake_block_t::fake_block_t() : block_t(FAKE) {} -scope_block_t::scope_block_t(block_type_t type) : block_t(type) -{ +scope_block_t::scope_block_t(block_type_t type) : block_t(type) { assert(type == BEGIN || type == TOP || type == SUBST); } -breakpoint_block_t::breakpoint_block_t() : block_t(BREAKPOINT) -{ -} +breakpoint_block_t::breakpoint_block_t() : block_t(BREAKPOINT) {} diff --git a/src/parser.h b/src/parser.h index d40ca16be..4a6823061 100644 --- a/src/parser.h +++ b/src/parser.h @@ -1,428 +1,348 @@ -/** \file parser.h - The fish parser. -*/ +// The fish parser. #ifndef FISH_PARSER_H #define FISH_PARSER_H +#include #include #include #include -#include #include "common.h" -#include "proc.h" #include "event.h" -#include "parse_tree.h" -#include "parse_constants.h" #include "expand.h" +#include "parse_constants.h" +#include "parse_tree.h" +#include "proc.h" class io_chain_t; -/** - event_blockage_t represents a block on events of the specified type -*/ -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 << EVENT_ANY)) return true; + if (iter->typemask & (1 << type)) return true; } return false; } - -/** - Types of blocks -*/ -enum block_type_t -{ - WHILE, /**< While loop block */ - FOR, /**< For loop block */ - IF, /**< If block */ - FUNCTION_DEF, /**< Function definition block */ - FUNCTION_CALL, /**< Function invocation block */ - FUNCTION_CALL_NO_SHADOW, /**< Function invocation block with no variable shadowing */ - SWITCH, /**< Switch block */ - FAKE, /**< Fake block */ - SUBST, /**< Command substitution scope */ - TOP, /**< Outermost block */ - BEGIN, /**< Unconditional block */ - SOURCE, /**< Block created by the . (source) builtin */ - EVENT, /**< Block created on event notifier invocation */ - BREAKPOINT, /**< Breakpoint block */ +/// Types of blocks. +enum block_type_t { + WHILE, /// While loop block + FOR, /// For loop block + IF, /// If block + FUNCTION_DEF, /// Function definition block + FUNCTION_CALL, /// Function invocation block + FUNCTION_CALL_NO_SHADOW, /// Function invocation block with no variable shadowing + SWITCH, /// Switch block + FAKE, /// Fake block + SUBST, /// Command substitution scope + TOP, /// Outermost block + BEGIN, /// Unconditional block + SOURCE, /// Block created by the . (source) builtin + EVENT, /// Block created on event notifier invocation + BREAKPOINT, /// Breakpoint block }; -/** Possible states for a loop */ -enum loop_status_t -{ - LOOP_NORMAL, /**< Current loop block executed as normal */ - LOOP_BREAK, /**< Current loop block should be removed */ - LOOP_CONTINUE, /**< Current loop block should be skipped */ +/// Possible states for a loop. +enum loop_status_t { + LOOP_NORMAL, /// current loop block executed as normal + LOOP_BREAK, /// current loop block should be removed + LOOP_CONTINUE, /// current loop block should be skipped }; -/** - block_t represents a block of commands. -*/ -struct block_t -{ -protected: - /** Protected constructor. Use one of the subclasses below. */ +/// block_t represents a block of commands. +struct block_t { + protected: + /// Protected constructor. Use one of the subclasses below. explicit block_t(block_type_t t); -private: - const block_type_t block_type; /**< Type of block. */ + private: + /// Type of block. + const block_type_t block_type; -public: - block_type_t type() const - { - return this->block_type; - } - - /** Description of the block, for debugging */ - wcstring description() const; - - bool skip; /**< Whether execution of the commands in this block should be skipped */ - int tok_pos; /**< The start index of the block */ - - node_offset_t node_offset; /* Offset of the node */ - - /** Status for the current loop block. Can be any of the values from the loop_status enum. */ + public: + /// Whether execution of the commands in this block should be skipped. + bool skip; + /// The start index of the block. + int tok_pos; + /// Offset of the node. + node_offset_t node_offset; + /// Status for the current loop block. Can be any of the values from the loop_status enum. enum loop_status_t loop_status; - - /** The job that is currently evaluated in the specified block. */ + /// The job that is currently evaluated in the specified block. job_t *job; - - /** Name of file that created this block. This string is intern'd. */ + /// Name of file that created this block. This string is intern'd. const wchar_t *src_filename; - - /** Line number where this block was created */ + /// Line number where this block was created. int src_lineno; - - /** Whether we should pop the environment variable stack when we're popped off of the block stack */ + /// Whether we should pop the environment variable stack when we're popped off of the block + /// stack. bool wants_pop_env; - /** List of event blocks. */ + block_type_t type() const { return this->block_type; } + + /// Description of the block, for debugging. + wcstring description() const; + + /// List of event blocks. event_blockage_list_t event_blocks; - /** Destructor */ + /// Destructor virtual ~block_t(); }; -struct if_block_t : public block_t -{ +struct if_block_t : public block_t { if_block_t(); }; -struct event_block_t : public block_t -{ +struct event_block_t : public block_t { event_t const event; explicit event_block_t(const event_t &evt); }; -struct function_block_t : public block_t -{ +struct function_block_t : public block_t { const process_t *process; wcstring name; function_block_t(const process_t *p, const wcstring &n, bool shadows); }; -struct source_block_t : public block_t -{ - const wchar_t * const source_file; +struct source_block_t : public block_t { + const wchar_t *const source_file; explicit source_block_t(const wchar_t *src); }; -struct for_block_t : public block_t -{ +struct for_block_t : public block_t { for_block_t(); }; -struct while_block_t : public block_t -{ +struct while_block_t : public block_t { while_block_t(); }; -struct switch_block_t : public block_t -{ +struct switch_block_t : public block_t { switch_block_t(); }; -struct fake_block_t : public block_t -{ +struct fake_block_t : public block_t { fake_block_t(); }; -struct scope_block_t : public block_t -{ - explicit scope_block_t(block_type_t type); //must be BEGIN, TOP or SUBST +struct scope_block_t : public block_t { + explicit scope_block_t(block_type_t type); // must be BEGIN, TOP or SUBST }; -struct breakpoint_block_t : public block_t -{ +struct breakpoint_block_t : public block_t { breakpoint_block_t(); }; -/** - Errors that can be generated by the parser -*/ -enum parser_error -{ - /** - No error - */ - NO_ERR=0, - /** - An error in the syntax - */ +/// Errors that can be generated by the parser. +enum parser_error { + /// No error. + NO_ERR = 0, + /// An error in the syntax. SYNTAX_ERROR, - /** - Error occured while evaluating commands - */ + /// Error occured while evaluating commands. EVAL_ERROR, - /** - Error while evaluating cmdsubst - */ + /// Error while evaluating cmdsubst. CMDSUBST_ERROR, }; -struct profile_item_t -{ - /** Time spent executing the specified command, including parse time for nested blocks. */ +struct profile_item_t { + /// Time spent executing the specified command, including parse time for nested blocks. int exec; - - /** Time spent parsing the specified command, including execution time for command substitutions. */ + /// Time spent parsing the specified command, including execution time for command + /// substitutions. int parse; - - /** The block level of the specified command. nested blocks and command substitutions both increase the block level. */ + /// The block level of the specified command. nested blocks and command substitutions both + /// increase the block level. size_t level; - - /** If the execution of this command was skipped. */ + /// If the execution of this command was skipped. bool skipped; - - /** The command string. */ + /// The command string. wcstring cmd; }; class parse_execution_context_t; class completion_t; -class parser_t -{ +class parser_t { friend class parse_execution_context_t; -private: - /** Indication that we should skip all blocks */ + + private: + /// Indication that we should skip all blocks. bool cancellation_requested; - - /** Indicates that we are within the process of initializing fish */ + /// Indicates that we are within the process of initializing fish. bool is_within_fish_initialization; - - /** Stack of execution contexts. We own these pointers and must delete them */ + /// Stack of execution contexts. We own these pointers and must delete them. std::vector execution_contexts; - - /** List of called functions, used to help prevent infinite recursion */ + /// List of called functions, used to help prevent infinite recursion. wcstring_list_t forbidden_function; - - /** The jobs associated with this parser */ + /// The jobs associated with this parser. job_list_t my_job_list; - - /** The list of blocks, allocated with new. It's our responsibility to delete these */ + /// The list of blocks, allocated with new. It's our responsibility to delete these. std::vector block_stack; - /** Gets a description of the block stack, for debugging */ + /// Gets a description of the block stack, for debugging. wcstring block_stack_description() const; - /** List of profile items, allocated with new */ + /// List of profile items, allocated with new. std::vector profile_items; - /* No copying allowed */ - parser_t(const parser_t&); - parser_t& operator=(const parser_t&); + // No copying allowed. + parser_t(const parser_t &); + parser_t &operator=(const parser_t &); - /** Adds a job to the beginning of the job list. */ + /// Adds a job to the beginning of the job list. void job_add(job_t *job); - /** - Returns the name of the currently evaluated function if we are - currently evaluating a function, null otherwise. This is tested by - moving down the block-scope-stack, checking every block if it is of - type FUNCTION_CALL. - */ + /// Returns the name of the currently evaluated function if we are currently evaluating a + /// function, null otherwise. This is tested by moving down the block-scope-stack, checking + /// every block if it is of type FUNCTION_CALL. const wchar_t *is_function() const; - /* Helper for stack_trace() */ + /// Helper for stack_trace(). void stack_trace_internal(size_t block_idx, wcstring *out) const; -public: - - /** Get the "principal" parser, whatever that is */ + public: + /// Get the "principal" parser, whatever that is. static parser_t &principal_parser(); - /** Indicates that execution of all blocks in the principal parser should stop. - This is called from signal handlers! - */ + /// Indicates that execution of all blocks in the principal parser should stop. This is called + /// from signal handlers! static void skip_all_blocks(); - /** Create a parser */ + /// Create a parser. parser_t(); - /** Global event blocks */ + /// Global event blocks. event_blockage_list_t global_event_blocks; - /** - Evaluate the expressions contained in cmd. - - \param cmd the string to evaluate - \param io io redirections to perform on all started jobs - \param block_type The type of block to push on the block stack - - \return 0 on success, 1 otherwise - */ + /// Evaluate the expressions contained in cmd. + /// + /// \param cmd the string to evaluate + /// \param io io redirections to perform on all started jobs + /// \param block_type The type of block to push on the block stack + /// + /// \return 0 on success, 1 otherwise int eval(const wcstring &cmd, const io_chain_t &io, enum block_type_t block_type); - - /** - Evaluate the expressions contained in cmd, which has been parsed into the given parse tree. This takes ownership of the tree. - */ - int eval_acquiring_tree(const wcstring &cmd, const io_chain_t &io, enum block_type_t block_type, moved_ref t); - /** Evaluates a block node at the given node offset in the topmost execution context */ + /// Evaluate the expressions contained in cmd, which has been parsed into the given parse tree. + /// This takes ownership of the tree. + int eval_acquiring_tree(const wcstring &cmd, const io_chain_t &io, enum block_type_t block_type, + moved_ref t); + + /// Evaluates a block node at the given node offset in the topmost execution context. int eval_block_node(node_offset_t node_idx, const io_chain_t &io, enum block_type_t block_type); - /** - Evaluate line as a list of parameters, i.e. tokenize it and perform parameter expansion and cmdsubst execution on the tokens. - The output is inserted into output. - Errors are ignored. + /// Evaluate line as a list of parameters, i.e. tokenize it and perform parameter expansion and + /// cmdsubst execution on the tokens. The output is inserted into output. Errors are ignored. + /// + /// \param arg_src String to evaluate as an argument list + /// \param flags Some expand flags to use + /// \param output List to insert output into + static void expand_argument_list(const wcstring &arg_src, expand_flags_t flags, + std::vector *output); - \param arg_src String to evaluate as an argument list - \param flags Some expand flags to use - \param output List to insert output into - */ - static void expand_argument_list(const wcstring &arg_src, expand_flags_t flags, std::vector *output); - - /** - Returns a string describing the current parser pisition in the format 'FILENAME (line LINE_NUMBER): LINE'. - Example: - - init.fish (line 127): ls|grep pancake - */ + /// Returns a string describing the current parser pisition in the format 'FILENAME (line + /// LINE_NUMBER): LINE'. Example: + /// + /// init.fish (line 127): ls|grep pancake wcstring current_line(); - /** Returns the current line number */ + /// Returns the current line number. int get_lineno() const; - /** Returns the block at the given index. 0 corresponds to the innermost block. Returns NULL when idx is at or equal to the number of blocks. */ + /// Returns the block at the given index. 0 corresponds to the innermost block. Returns NULL + /// when idx is at or equal to the number of blocks. const block_t *block_at_index(size_t idx) const; block_t *block_at_index(size_t idx); - /** Returns the current (innermost) block */ + /// Returns the current (innermost) block. const block_t *current_block() const; block_t *current_block(); - /** Count of blocks */ - size_t block_count() const - { - return block_stack.size(); - } + /// Count of blocks. + size_t block_count() const { return block_stack.size(); } - /** Get the list of jobs */ - job_list_t &job_list() - { - return my_job_list; - } + /// Get the list of jobs. + job_list_t &job_list() { return my_job_list; } - /* Hackish. In order to correctly report the origin of code with no associated file, we need to know whether it's run during initialization or not. */ + // Hackish. In order to correctly report the origin of code with no associated file, we need to + // know whether it's run during initialization or not. void set_is_within_fish_initialization(bool flag); - /** Pushes the block. pop_block will call delete on it. */ + /// Pushes the block. pop_block will call delete on it. void push_block(block_t *newv); - /** Remove the outermost block namespace */ + /// Remove the outermost block namespace. void pop_block(); - /** Remove the outermost block, asserting it's the given one */ + /// Remove the outermost block, asserting it's the given one. void pop_block(const block_t *b); - /** Return a description of the given blocktype */ + /// Return a description of the given blocktype. const wchar_t *get_block_desc(int block) const; - /** Removes a job */ + /// Removes a job. bool job_remove(job_t *job); - /** Promotes a job to the front of the list */ + /// Promotes a job to the front of the list. void job_promote(job_t *job); - /** Return the job with the specified job id. If id is 0 or less, return the last job used. */ + /// Return the job with the specified job id. If id is 0 or less, return the last job used. job_t *job_get(int job_id); - /** Returns the job with the given pid */ + /// Returns the job with the given pid. job_t *job_get_from_pid(int pid); - /* Returns a new profile item if profiling is active. The caller should fill it in. The parser_t will clean it up. */ + /// Returns a new profile item if profiling is active. The caller should fill it in. The + /// parser_t will clean it up. profile_item_t *create_profile_item(); - /** - Test if the specified string can be parsed, or if more bytes need - to be read first. The result will have the PARSER_TEST_ERROR bit - set if there is a syntax error in the code, and the - PARSER_TEST_INCOMPLETE bit set if the code contains unclosed - blocks. + /// Test if the specified string can be parsed, or if more bytes need to be read first. The + /// result will have the PARSER_TEST_ERROR bit set if there is a syntax error in the code, and + /// the PARSER_TEST_INCOMPLETE bit set if the code contains unclosed blocks. + /// + /// \param buff the text buffer to test + /// \param block_level if non-null, the block nesting level will be filled out into this array + /// \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. + void get_backtrace(const wcstring &src, const parse_error_list_t &errors, + wcstring *output) const; - \param buff the text buffer to test - \param block_level if non-null, the block nesting level will be filled out into this array - \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 - */ - void get_backtrace(const wcstring &src, const parse_error_list_t &errors, wcstring *output) const; + /// Detect errors in the specified string when parsed as an argument list. Returns true if an + /// error occurred. + bool detect_errors_in_argument_list(const wcstring &arg_list_src, wcstring *out_err, + const wchar_t *prefix); - /** - Detect errors in the specified string when parsed as an argument list. Returns true if an error occurred. - */ - bool detect_errors_in_argument_list(const wcstring &arg_list_src, wcstring *out_err, 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. - */ + /// 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); - /** - Undo last call to parser_forbid_function(). - */ + /// Undo last call to parser_forbid_function(). void allow_function(); - /** - Output profiling data to the given filename - */ + /// Output profiling data to the given filename. void emit_profiling(const char *path) const; - /** - Returns the file currently evaluated by the parser. This can be - different than reader_current_filename, e.g. if we are evaulating a - function defined in a different file than the one curently read. - */ + /// Returns the file currently evaluated by the parser. This can be different than + /// reader_current_filename, e.g. if we are evaulating a function defined in a different file + /// than the one curently read. const wchar_t *current_filename() const; - /** - Return a string representing the current stack trace - */ + /// Return a string representing the current stack trace. wcstring stack_trace() const; }; diff --git a/src/parser_keywords.cpp b/src/parser_keywords.cpp index 5f9710ac1..340a886f2 100644 --- a/src/parser_keywords.cpp +++ b/src/parser_keywords.cpp @@ -1,56 +1,22 @@ -/** \file parser_keywords.c - -Functions having to do with parser keywords, like testing if a function is a block command. -*/ -#include "fallback.h" // IWYU pragma: keep -#include "common.h" +// Functions having to do with parser keywords, like testing if a function is a block command. #include "parser_keywords.h" +#include "common.h" +#include "fallback.h" // IWYU pragma: keep -bool parser_keywords_skip_arguments(const wcstring &cmd) -{ - return contains(cmd, - L"else", - L"begin"); +bool parser_keywords_skip_arguments(const wcstring &cmd) { + 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"); - + contains(cmd, L"command", L"builtin", L"while", L"exec", L"if", L"and", L"or", L"not"); } -bool parser_keywords_is_block(const wcstring &word) -{ - return contains(word, - L"for", - L"while", - L"if", - L"function", - L"switch", - L"begin"); +bool parser_keywords_is_block(const wcstring &word) { + return contains(word, L"for", L"while", L"if", L"function", L"switch", L"begin"); } -bool parser_keywords_is_reserved(const wcstring &word) -{ - return parser_keywords_is_block(word) || - parser_keywords_is_subcommand(word) || - contains(word, - L"end", - L"case", - L"else", - L"return", - L"continue", - L"break"); +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"); } - diff --git a/src/parser_keywords.h b/src/parser_keywords.h index d9637323a..f922b8262 100644 --- a/src/parser_keywords.h +++ b/src/parser_keywords.h @@ -1,7 +1,4 @@ -/** \file parser_keywords.h - -Functions having to do with parser keywords, like testing if a function is a block command. -*/ +// Functions having to do with parser keywords, like testing if a function is a block command. #ifndef FISH_PARSER_KEYWORD_H #define FISH_PARSER_KEYWORD_H @@ -9,38 +6,27 @@ Functions having to do with parser keywords, like testing if a function is a blo #include "common.h" -/** - Tests if the specified commands parameters should be interpreted as another command, which will be true if the command is either 'command', 'exec', 'if', 'while', or 'builtin'. This does not handle "else if" which is more complicated. - - \param cmd The command name to test - \return 1 of the command parameter is a command, 0 otherwise -*/ - +/// Tests if the specified commands parameters should be interpreted as another command, which will +/// be true if the command is either 'command', 'exec', 'if', 'while', or 'builtin'. This does not +/// handle "else if" which is more complicated. +/// +/// \param cmd The command name to test +/// \return 1 of the command parameter is a command, 0 otherwise bool parser_keywords_is_subcommand(const wcstring &cmd); -/** - Tests if the specified command is a reserved word, i.e. if it is - the name of one of the builtin functions that change the block or - command scope, like 'for', 'end' or 'command' or 'exec'. These - functions may not be overloaded, so their names are reserved. - - \param word The command name to test - \return 1 of the command parameter is a command, 0 otherwise -*/ +/// Tests if the specified command is a reserved word, i.e. if it is the name of one of the builtin +/// functions that change the block or command scope, like 'for', 'end' or 'command' or 'exec'. +/// These functions may not be overloaded, so their names are reserved. +/// +/// \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); -/** - Test if the specified string is command that opens a new block -*/ - +/// Test if the specified string is command that opens a new block. 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 -*/ +/// 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); - #endif From 8d6b88eb5d0ba6b422d2e37873b4d3026d2fbcd0 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Mon, 2 May 2016 21:15:43 -0700 Subject: [PATCH 203/363] restyle path module to match project style Reduces lint errors from 30 to 21 (-30%). Line count from 597 to 481 (-19%). Another step in resolving issue #2902. --- src/path.cpp | 415 ++++++++++++++++++++------------------------------- src/path.h | 125 +++++++--------- 2 files changed, 212 insertions(+), 328 deletions(-) diff --git a/src/path.cpp b/src/path.cpp index ebe14165a..d80ffadfc 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -1,118 +1,90 @@ -#include +// Directory utilities. This library contains functions for locating configuration directories, for +// testing if a command with a given name can be found in the PATH, and various other path-related +// issues. +#include +#include #include #include -#include -#include +#include #include #include -#include "fallback.h" // IWYU pragma: keep #include "common.h" #include "env.h" -#include "wutil.h" // IWYU pragma: keep -#include "path.h" #include "expand.h" +#include "fallback.h" // IWYU pragma: keep +#include "path.h" +#include "wutil.h" // IWYU pragma: keep -/** - Unexpected error in path_get_path() -*/ -#define MISSING_COMMAND_ERR_MSG _( L"Error while searching for command '%ls'" ) +/// Unexpected error in path_get_path(). +#define MISSING_COMMAND_ERR_MSG _(L"Error while searching for command '%ls'") -static bool path_get_path_core(const wcstring &cmd, wcstring *out_path, const env_var_t &bin_path_var) -{ +static bool path_get_path_core(const wcstring &cmd, wcstring *out_path, + const env_var_t &bin_path_var) { int err = ENOENT; debug(3, L"path_get_path( '%ls' )", cmd.c_str()); - /* 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 the command has a slash, it must be a full path. + if (cmd.find(L'/') != wcstring::npos) { + if (waccess(cmd, X_OK) == 0) { struct stat buff; - if (wstat(cmd, &buff)) - { + if (wstat(cmd, &buff)) { return false; } - if (S_ISREG(buff.st_mode)) - { - if (out_path) - out_path->assign(cmd); + if (S_ISREG(buff.st_mode)) { + if (out_path) out_path->assign(cmd); return true; - } - else - { + } else { errno = EACCES; return false; } - } - else - { + } else { return false; } - } - else - { + } 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")) - { + } else { + if (contains(PREFIX L"/bin", L"/bin", L"/usr/bin")) { bin_path = L"/bin" ARRAY_SEP_STR L"/usr/bin"; - } - else - { + } 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)) - { - if (nxt_path.empty()) - continue; + while (tokenizer.next(nxt_path)) { + if (nxt_path.empty()) continue; append_path_component(nxt_path, cmd); - if (waccess(nxt_path, X_OK)==0) - { + if (waccess(nxt_path, X_OK) == 0) { struct stat buff; - if (wstat(nxt_path, &buff)==-1) - { - if (errno != EACCES) - { + 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); + if (S_ISREG(buff.st_mode)) { + if (out_path) out_path->swap(nxt_path); return true; } err = EACCES; - } - else - { - switch (errno) - { + } else { + switch (errno) { case ENOENT: case ENAMETOOLONG: case EACCES: - case ENOTDIR: + case ENOTDIR: { break; - default: - { - debug(1, - MISSING_COMMAND_ERR_MSG, - nxt_path.c_str()); + } + default: { + debug(1, MISSING_COMMAND_ERR_MSG, nxt_path.c_str()); wperror(L"access"); } } @@ -124,69 +96,53 @@ static bool path_get_path_core(const wcstring &cmd, wcstring *out_path, const en return false; } -bool path_get_path(const wcstring &cmd, wcstring *out_path, const env_vars_snapshot_t &vars) -{ +bool path_get_path(const wcstring &cmd, wcstring *out_path, const env_vars_snapshot_t &vars) { return path_get_path_core(cmd, out_path, vars.get(L"PATH")); } -bool path_get_path(const wcstring &cmd, wcstring *out_path) -{ +bool path_get_path(const wcstring &cmd, wcstring *out_path) { return path_get_path_core(cmd, out_path, env_get_string(L"PATH")); } -bool path_get_cdpath(const wcstring &dir, wcstring *out, const wchar_t *wd, const env_vars_snapshot_t &env_vars) -{ +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; + if (dir.empty()) return false; - if (wd) - { + if (wd) { size_t len = wcslen(wd); assert(wd[len - 1] == L'/'); } wcstring_list_t paths; - if (dir.at(0) == L'/') - { - /* Absolute path */ + 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"..") - { - /* Path is relative to the working directory */ + } 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); + if (wd) path.append(wd); path.append(dir); paths.push_back(path); - } - else - { - // Respect CDPATH + } else { + // Respect CDPATH. env_var_t path = env_vars.get(L"CDPATH"); - if (path.missing_or_empty()) - path = L"."; //We'll change this to the wd if we have one + if (path.missing_or_empty()) path = L"."; // we'll change this to the wd if we have one wcstring nxt_path; wcstokenizer tokenizer(path, ARRAY_SEP_STR); - while (tokenizer.next(nxt_path)) - { - - if (nxt_path == L"." && wd != NULL) - { - // nxt_path is just '.', and we have a working directory, so use the wd instead - // TODO: if nxt_path starts with ./ we need to replace the . with the wd + while (tokenizer.next(nxt_path)) { + if (nxt_path == L"." && wd != NULL) { + // nxt_path is just '.', and we have a working directory, so use the wd instead. + // TODO: if nxt_path starts with ./ we need to replace the . with the wd. nxt_path = wd; } expand_tilde(nxt_path); -// debug( 2, L"woot %ls\n", expanded_path.c_str() ); + // debug( 2, L"woot %ls\n", expanded_path.c_str() ); - if (nxt_path.empty()) - continue; + if (nxt_path.empty()) continue; wcstring whole_path = nxt_path; append_path_component(whole_path, dir); @@ -195,300 +151,245 @@ 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) - { + 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) - { - if (S_ISDIR(buf.st_mode)) - { + if (wstat(dir, &buf) == 0) { + if (S_ISDIR(buf.st_mode)) { success = true; - if (out) - out->assign(dir); + if (out) out->assign(dir); break; - } - else - { + } else { err = ENOTDIR; } } } - if (! success) - errno = err; + if (!success) errno = err; return success; } -bool path_can_be_implicit_cd(const wcstring &path, wcstring *out_path, const wchar_t *wd, const env_vars_snapshot_t &vars) -{ +bool path_can_be_implicit_cd(const wcstring &path, wcstring *out_path, const wchar_t *wd, + const env_vars_snapshot_t &vars) { wcstring exp_path = path; expand_tilde(exp_path); bool result = false; - if (string_prefixes_string(L"/", exp_path) || - string_prefixes_string(L"./", exp_path) || - string_prefixes_string(L"../", exp_path) || - string_suffixes_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) */ + if (string_prefixes_string(L"/", exp_path) || string_prefixes_string(L"./", exp_path) || + string_prefixes_string(L"../", exp_path) || string_suffixes_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); } return result; } -/* If the given path looks like it's relative to the working directory, then prepend that working directory. This operates on unescaped paths only (so a ~ means a literal ~) */ -wcstring path_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 / */ +// If the given path looks like it's relative to the working directory, then prepend that working +// directory. This operates on unescaped paths only (so a ~ means a literal ~). +wcstring path_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)) - { + switch (path.at(0)) { case L'/': - case HOME_DIRECTORY: + case HOME_DIRECTORY: { prepend_wd = false; break; - default: + } + default: { prepend_wd = true; break; + } } - - if (! prepend_wd) - { - /* No need to prepend the wd, so just return the path we were given */ + + if (!prepend_wd) { + // No need to prepend the wd, so just return the path we were given. return path; - } - else - { - /* Remove up to one ./ */ + } 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)) - { + + // Removing leading /s. + while (string_prefixes_string(L"/", path_component)) { path_component.erase(0, 1); } - - /* Construct and return a new path */ + + // Construct and return a new path. wcstring new_path = working_directory; append_path_component(new_path, path_component); return new_path; } } - - -static wcstring path_create_config() -{ +static wcstring path_create_config() { bool done = false; wcstring res; - const env_var_t xdg_dir = env_get_string(L"XDG_CONFIG_HOME"); - if (! xdg_dir.missing()) - { + 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)) - { + if (!create_directory(res)) { done = true; } - } - else - { + } else { const env_var_t home = env_get_string(L"HOME"); - if (! home.missing()) - { + if (!home.missing()) { res = home + L"/.config/fish"; - if (!create_directory(res)) - { + if (!create_directory(res)) { done = true; } } } - if (! done) - { + if (!done) { res.clear(); - debug(0, _(L"Unable to create a configuration directory for fish. Your personal settings will not be saved. Please set the $XDG_CONFIG_HOME variable to a directory where the current user has write access.")); + debug(0, _(L"Unable to create a configuration directory for fish. Your personal settings " + L"will not be saved. Please set the $XDG_CONFIG_HOME variable to a directory " + L"where the current user has write access.")); } return res; } -static wcstring path_create_data() -{ +static wcstring path_create_data() { bool done = false; wcstring res; - const env_var_t xdg_dir = env_get_string(L"XDG_DATA_HOME"); - if (! xdg_dir.missing()) - { + const env_var_t xdg_dir = env_get_string(L"XDG_DATA_HOME"); + if (!xdg_dir.missing()) { res = xdg_dir + L"/fish"; - if (!create_directory(res)) - { + if (!create_directory(res)) { done = true; } - } - else - { + } else { const env_var_t home = env_get_string(L"HOME"); - if (! home.missing()) - { + if (!home.missing()) { res = home + L"/.local/share/fish"; - if (!create_directory(res)) - { + if (!create_directory(res)) { done = true; } } } - if (! done) - { + if (!done) { res.clear(); - debug(0, _(L"Unable to create a data directory for fish. Your history will not be saved. Please set the $XDG_DATA_HOME variable to a directory where the current user has write access.")); + debug(0, _(L"Unable to create a data directory for fish. Your history will not be saved. " + L"Please set the $XDG_DATA_HOME variable to a directory where the current user " + L"has write access.")); } return res; } -/* Cache the config path */ -bool path_get_config(wcstring &path) -{ +/// Cache the config path. +bool path_get_config(wcstring &path) { static const wcstring result = path_create_config(); path = result; - return ! result.empty(); + return !result.empty(); } -/* Cache the data path */ -bool path_get_data(wcstring &path) -{ +/// Cache the data path. +bool path_get_data(wcstring &path) { static const wcstring result = path_create_data(); path = result; - return ! result.empty(); + return !result.empty(); } -__attribute__((unused)) -static void replace_all(wcstring &str, const wchar_t *needle, const wchar_t *replacement) -{ +__attribute__((unused)) static void replace_all(wcstring &str, const wchar_t *needle, + const wchar_t *replacement) { 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) -{ - // Ignore trailing slashes, unless it's the first character +void path_make_canonical(wcstring &path) { + // Ignore trailing slashes, unless it's the first character. size_t len = path.size(); - while (len > 1 && path.at(len - 1) == L'/') - len--; + while (len > 1 && path.at(len - 1) == L'/') len--; - // Turn runs of slashes into a single slash + // Turn runs of slashes into a single slash. size_t trailing = 0; bool prev_was_slash = false; - for (size_t leading = 0; leading < len; leading++) - { + for (size_t leading = 0; leading < len; leading++) { wchar_t c = path.at(leading); bool is_slash = (c == '/'); - if (! prev_was_slash || ! is_slash) - { - // This is either the first slash in a run, or not a slash at all + if (!prev_was_slash || !is_slash) { + // This is either the first slash in a run, or not a slash at all. path.at(trailing++) = c; } prev_was_slash = is_slash; } assert(trailing <= len); - if (trailing < len) - path.resize(trailing); + if (trailing < len) path.resize(trailing); } -bool paths_are_equivalent(const wcstring &p1, const wcstring &p2) -{ - if (p1 == p2) - return true; +bool paths_are_equivalent(const wcstring &p1, const wcstring &p2) { + if (p1 == p2) return true; size_t len1 = p1.size(), len2 = p2.size(); - // Ignore trailing slashes after the first character + // Ignore trailing slashes after the first character. while (len1 > 1 && p1.at(len1 - 1) == L'/') len1--; while (len2 > 1 && p2.at(len2 - 1) == L'/') len2--; // Start walking size_t idx1 = 0, idx2 = 0; - while (idx1 < len1 && idx2 < len2) - { + while (idx1 < len1 && idx2 < len2) { wchar_t c1 = p1.at(idx1), c2 = p2.at(idx2); - // If the characters are different, the strings are not equivalent - if (c1 != c2) - break; + // If the characters are different, the strings are not equivalent. + if (c1 != c2) break; idx1++; idx2++; - // If the character was a slash, walk forwards until we hit the end of the string, or a non-slash - // Note the first condition is invariant within the loop + // If the character was a slash, walk forwards until we hit the end of the string, or a + // non-slash. Note the first condition is invariant within the loop. while (c1 == L'/' && idx1 < len1 && p1.at(idx1) == L'/') idx1++; while (c2 == L'/' && idx2 < len2 && p2.at(idx2) == L'/') idx2++; } - // We matched if we consumed all of the characters in both strings + // We matched if we consumed all of the characters in both strings. return idx1 == len1 && idx2 == len2; } -bool path_is_valid(const wcstring &path, const wcstring &working_directory) -{ +bool path_is_valid(const wcstring &path, const wcstring &working_directory) { bool path_is_valid; - /* Some special paths are always valid */ - if (path.empty()) - { + // Some special paths are always valid. + 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"../") - { - path_is_valid = (! working_directory.empty() && working_directory != L"/"); - } - else if (path.at(0) != '/') - { - /* Prepend the working directory. Note that we know path is not empty here. */ + } else if (path == L".." || path == L"../") { + path_is_valid = (!working_directory.empty() && working_directory != L"/"); + } 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 - { - /* Simple check */ + } 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) -{ - if (paths_are_equivalent(path1, path2)) - return true; +bool paths_are_same_file(const wcstring &path1, const wcstring &path2) { + if (paths_are_equivalent(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; } } diff --git a/src/path.h b/src/path.h index ae958b689..1415859a2 100644 --- a/src/path.h +++ b/src/path.h @@ -1,103 +1,86 @@ -/** \file path.h - - Directory utilities. This library contains functions for locating - configuration directories, for testing if a command with a given - name can be found in the PATH, and various other path-related - issues. -*/ +// Directory utilities. This library contains functions for locating configuration directories, for +// testing if a command with a given name can be found in the PATH, and various other path-related +// issues. #ifndef FISH_PATH_H #define FISH_PATH_H -#include #include +#include #include "common.h" #include "env.h" -/** - Return value for path_cdpath_get when locatied a rotten symlink - */ +/// Return value for path_cdpath_get when locatied a rotten symlink. #define EROTTEN 1 -/** - Returns the user configuration directory for fish. If the directory - or one of its parents doesn't exist, they are first created. - - \param path The directory as an out param - \return whether the directory was returned successfully -*/ +/// Returns the user configuration directory for fish. If the directory or one of its parents +/// doesn't exist, they are first created. +/// +/// \param path The directory as an out param +/// \return whether the directory was returned successfully bool path_get_config(wcstring &path); -/** - Returns the user data directory for fish. If the directory - or one of its parents doesn't exist, they are first created. - - Volatile files presumed to be local to the machine, - such as the fish_history and all the generated_completions, - will be stored in this directory. - - \param path The directory as an out param - \return whether the directory was returned successfully -*/ +/// Returns the user data directory for fish. If the directory or one of its parents doesn't exist, +/// they are first created. +/// +/// Volatile files presumed to be local to the machine, such as the fish_history and all the +/// generated_completions, will be stored in this directory. +/// +/// \param path The directory as an out param +/// \return whether the directory was returned successfully bool path_get_data(wcstring &path); -/** - Finds the full path of an executable. Returns YES if successful. - - \param cmd The name of the executable. - \param output_or_NULL If non-NULL, store the full path. - \param vars The environment variables snapshot to use - \return 0 if the command can not be found, the path of the command otherwise. The result should be freed with free(). -*/ -bool path_get_path(const wcstring &cmd, - wcstring *output_or_NULL, +/// Finds the full path of an executable. Returns YES if successful. +/// +/// \param cmd The name of the executable. +/// \param output_or_NULL If non-NULL, store the full path. +/// \param vars The environment variables snapshot to use +/// \return 0 if the command can not be found, the path of the command otherwise. The result should +/// be freed with free(). +bool path_get_path(const wcstring &cmd, wcstring *output_or_NULL, const env_vars_snapshot_t &vars = env_vars_snapshot_t::current()); -/** - Returns the full path of the specified directory, using the CDPATH - variable as a list of base directories for relative paths. The - returned string is allocated using halloc and the specified - context. - - If no valid path is found, null is returned and errno is set to - ENOTDIR if at least one such path was found, but it did not point - to a directory, EROTTEN if a arotten symbolic link was found, or - ENOENT if no file of the specified name was found. If both a rotten - symlink and a file are found, it is undefined which error status - will be returned. - - \param dir The name of the directory. - \param out_or_NULL If non-NULL, return the path to the resolved directory - \param wd The working directory, or NULL to use the default. The working directory should have a slash appended at the end. - \param vars The environment variable snapshot to use (for the CDPATH variable) - \return 0 if the command can not be found, the path of the command otherwise. The path should be free'd with free(). -*/ -bool path_get_cdpath(const wcstring &dir, - wcstring *out_or_NULL, - const wchar_t *wd = NULL, +/// Returns the full path of the specified directory, using the CDPATH variable as a list of base +/// directories for relative paths. The returned string is allocated using halloc and the specified +/// context. +/// +/// If no valid path is found, null is returned and errno is set to ENOTDIR if at least one such +/// path was found, but it did not point to a directory, EROTTEN if a arotten symbolic link was +/// found, or ENOENT if no file of the specified name was found. If both a rotten symlink and a file +/// are found, it is undefined which error status will be returned. +/// +/// \param dir The name of the directory. +/// \param out_or_NULL If non-NULL, return the path to the resolved directory +/// \param wd The working directory, or NULL to use the default. The working directory should have a +/// slash appended at the end. +/// \param vars The environment variable snapshot to use (for the CDPATH variable) +/// \return 0 if the command can not be found, the path of the command otherwise. The path should be +/// free'd with free(). +bool path_get_cdpath(const wcstring &dir, wcstring *out_or_NULL, const wchar_t *wd = NULL, const env_vars_snapshot_t &vars = env_vars_snapshot_t::current()); -/** Returns whether the path can be used for an implicit cd command; if so, also returns the path by reference (if desired). This requires it to start with one of the allowed prefixes (., .., ~) and resolve to a directory. */ -bool path_can_be_implicit_cd(const wcstring &path, - wcstring *out_path = NULL, +/// Returns whether the path can be used for an implicit cd command; if so, also returns the path by +/// reference (if desired). This requires it to start with one of the allowed prefixes (., .., ~) +/// and resolve to a directory. +bool path_can_be_implicit_cd(const wcstring &path, wcstring *out_path = NULL, const wchar_t *wd = NULL, const env_vars_snapshot_t &vars = env_vars_snapshot_t::current()); -/** - Remove double slashes and trailing slashes from a path, - e.g. transform foo//bar/ into foo/bar. The string is modified in-place. - */ +/// 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); -/** Check if two paths are equivalent, which means to ignore runs of multiple slashes (or trailing slashes) */ +/// Check if two paths are equivalent, which means to ignore runs of multiple slashes (or trailing +/// slashes). bool paths_are_equivalent(const wcstring &p1, const wcstring &p2); bool path_is_valid(const wcstring &path, const wcstring &working_directory); -/** Returns whether the two paths refer to the same file */ +/// Returns whether the two paths refer to the same file. bool paths_are_same_file(const wcstring &path1, const wcstring &path2); -/* If the given path looks like it's relative to the working directory, then prepend that working directory. This operates on unescaped paths only (so a ~ means a literal ~) */ +/// If the given path looks like it's relative to the working directory, then prepend that working +/// directory. This operates on unescaped paths only (so a ~ means a literal ~). wcstring path_apply_working_directory(const wcstring &path, const wcstring &working_directory); #endif From dc8d31a12abc2dbd2aebd7a9c9b34207110c1233 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Mon, 2 May 2016 21:28:06 -0700 Subject: [PATCH 204/363] restyle postfork module to match project style Reduces lint errors from 37 to 20 (-46%). Line count from 670 to 566 (-15%). Another step in resolving issue #2902. --- src/postfork.cpp | 474 +++++++++++++++++++---------------------------- src/postfork.h | 74 ++++---- 2 files changed, 222 insertions(+), 326 deletions(-) diff --git a/src/postfork.cpp b/src/postfork.cpp index f2851eb05..cb0760b44 100644 --- a/src/postfork.cpp +++ b/src/postfork.cpp @@ -1,10 +1,6 @@ -/** \file postfork.cpp - - Functions that we may safely call after fork(). -*/ - -#include +// Functions that we may safely call after fork(). #include +#include #include #include #include @@ -15,67 +11,61 @@ #endif #include "common.h" -#include "proc.h" -#include "wutil.h" // IWYU pragma: keep -#include "signal.h" -#include "postfork.h" -#include "iothread.h" #include "exec.h" #include "io.h" +#include "iothread.h" +#include "postfork.h" +#include "proc.h" +#include "signal.h" +#include "wutil.h" // IWYU pragma: keep #ifndef JOIN_THREADS_BEFORE_FORK #define JOIN_THREADS_BEFORE_FORK 0 #endif -/** The number of times to try to call fork() before giving up */ +/// The number of times to try to call fork() before giving up. #define FORK_LAPS 5 -/** The number of nanoseconds to sleep between attempts to call fork() */ +/// The number of nanoseconds to sleep between attempts to call fork(). #define FORK_SLEEP_TIME 1000000 -/** Base open mode to pass to calls to open */ +/// Base open mode to pass to calls to open. #define OPEN_MASK 0666 -/** fork error message */ +/// Fork error message. #define FORK_ERROR "Could not create child process - exiting" -/** file redirection clobbering error message */ +/// File redirection clobbering error message. #define NOCLOB_ERROR "The file '%s' already exists" -/** file redirection error message */ +/// File redirection error message. #define FILE_ERROR "An error occurred while redirecting file '%s'" -/** file descriptor redirection error message */ -#define FD_ERROR "An error occurred while redirecting file descriptor %s" +/// File descriptor redirection error message. +#define FD_ERROR "An error occurred while redirecting file descriptor %s" -/** pipe error */ +/// Pipe error message. #define LOCAL_PIPE_ERROR "An error occurred while setting up pipe" static bool log_redirections = false; -/* Cover for debug_safe that can take an int. The format string should expect a %s */ -static void debug_safe_int(int level, const char *format, int val) -{ +/// Cover for debug_safe that can take an int. The format string should expect a %s. +static void debug_safe_int(int level, const char *format, int val) { char buff[128]; format_long_safe(buff, val); debug_safe(level, format, buff); } -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; - if (job_get_flag(j, JOB_CONTROL)) - { - if (!j->pgid) - { + if (job_get_flag(j, JOB_CONTROL)) { + if (!j->pgid) { j->pgid = p->pid; } - if (setpgid(p->pid, j->pgid)) - { - if (getpgid(p->pid) != j->pgid && print_errors) - { + 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]; @@ -90,29 +80,20 @@ int set_child_group(job_t *j, process_t *p, int print_errors) narrow_string_safe(argv0, p->argv0()); narrow_string_safe(command, j->command_wcstr()); - debug_safe(1, - "Could not send process %s, '%s' in job %s, '%s' from group %s to group %s", - pid_buff, - argv0, - job_id_buff, - command, - 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, argv0, job_id_buff, command, getpgid_buff, job_pgid_buff); safe_perror("setpgid"); res = -1; } } - } - else - { + } else { j->pgid = getpid(); } - if (job_get_flag(j, JOB_TERMINAL) && job_get_flag(j, JOB_FOREGROUND)) - { - if (tcsetpgrp(0, j->pgid) && print_errors) - { + if (job_get_flag(j, JOB_TERMINAL) && job_get_flag(j, JOB_FOREGROUND)) { + if (tcsetpgrp(0, j->pgid) && print_errors) { char job_id_buff[64]; char command_buff[64]; format_long_safe(job_id_buff, j->job_id); @@ -126,73 +107,51 @@ int set_child_group(job_t *j, process_t *p, int print_errors) return res; } -/** - Set up a childs io redirections. Should only be called by - setup_child_process(). Does the following: First it closes any open - file descriptors not related to the child by calling - close_unused_internal_pipes() and closing the universal variable - server file descriptor. It then goes on to perform all the - redirections described by \c io. - - \param io the list of IO redirections for the child - - \return 0 on sucess, -1 on failiure -*/ -static int handle_child_io(const io_chain_t &io_chain) -{ - for (size_t idx = 0; idx < io_chain.size(); idx++) - { +/// Set up a childs io redirections. Should only be called by setup_child_process(). Does the +/// following: First it closes any open file descriptors not related to the child by calling +/// close_unused_internal_pipes() and closing the universal variable server file descriptor. It then +/// goes on to perform all the redirections described by \c io. +/// +/// \param io the list of IO redirections for the child +/// +/// \return 0 on sucess, -1 on failure +static int handle_child_io(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).get(); - if (io->io_mode == IO_FD && io->fd == static_cast(io)->old_fd) - { + if (io->io_mode == IO_FD && io->fd == static_cast(io)->old_fd) { continue; } - switch (io->io_mode) - { - case IO_CLOSE: - { + switch (io->io_mode) { + case IO_CLOSE: { if (log_redirections) fprintf(stderr, "%d: close %d\n", getpid(), io->fd); - if (close(io->fd)) - { + if (close(io->fd)) { debug_safe_int(0, "Failed to close file descriptor %s", io->fd); safe_perror("close"); } break; } - case IO_FILE: - { - // Here we definitely do not want to set CLO_EXEC because our child needs access + case IO_FILE: { + // Here we definitely do not want to set CLO_EXEC because our child needs access. CAST_INIT(const io_file_t *, io_file, io); int tmp = open(io_file->filename_cstr, io_file->flags, OPEN_MASK); - if (tmp < 0) - { - if ((io_file->flags & O_EXCL) && - (errno ==EEXIST)) - { + if (tmp < 0) { + if ((io_file->flags & O_EXCL) && (errno == EEXIST)) { debug_safe(1, NOCLOB_ERROR, io_file->filename_cstr); - } - else - { + } else { debug_safe(1, FILE_ERROR, io_file->filename_cstr); safe_perror("open"); } return -1; - } - else if (tmp != io->fd) - { - /* - This call will sometimes fail, but that is ok, - this is just a precausion. - */ + } 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); + if (dup2(tmp, io->fd) == -1) { + debug_safe_int(1, FD_ERROR, io->fd); safe_perror("dup2"); exec_close(tmp); return -1; @@ -202,20 +161,15 @@ static int handle_child_io(const io_chain_t &io_chain) break; } - case IO_FD: - { + case IO_FD: { int old_fd = static_cast(io)->old_fd; - if (log_redirections) fprintf(stderr, "%d: fd dup %d to %d\n", getpid(), old_fd, io->fd); + if (log_redirections) + fprintf(stderr, "%d: fd dup %d to %d\n", getpid(), old_fd, io->fd); - /* - This call will sometimes fail, but that is ok, - this is just a precausion. - */ + // This call will sometimes fail, but that is ok, this is just a precausion. close(io->fd); - - if (dup2(old_fd, io->fd) == -1) - { + if (dup2(old_fd, io->fd) == -1) { debug_safe_int(1, FD_ERROR, io->fd); safe_perror("dup2"); return -1; @@ -224,88 +178,72 @@ static int handle_child_io(const io_chain_t &io_chain) } case IO_BUFFER: - case IO_PIPE: - { + case IO_PIPE: { CAST_INIT(const io_pipe_t *, io_pipe, io); - /* 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). */ + // 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_pipe->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->pipe_fd[0], - io->pipe_fd[1]); - */ - if (log_redirections) fprintf(stderr, "%d: %s dup %d to %d\n", getpid(), io->io_mode == IO_BUFFER ? "buffer" : "pipe", io_pipe->pipe_fd[write_pipe_idx], io->fd); - if (dup2(io_pipe->pipe_fd[write_pipe_idx], io->fd) != io->fd) - { +#if 0 + 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->pipe_fd[0], + io->pipe_fd[1]); +#endif + if (log_redirections) + fprintf(stderr, "%d: %s dup %d to %d\n", getpid(), + io->io_mode == IO_BUFFER ? "buffer" : "pipe", + io_pipe->pipe_fd[write_pipe_idx], io->fd); + if (dup2(io_pipe->pipe_fd[write_pipe_idx], io->fd) != io->fd) { debug_safe(1, LOCAL_PIPE_ERROR); safe_perror("dup2"); return -1; } - if (io_pipe->pipe_fd[0] >= 0) - exec_close(io_pipe->pipe_fd[0]); - if (io_pipe->pipe_fd[1] >= 0) - exec_close(io_pipe->pipe_fd[1]); + if (io_pipe->pipe_fd[0] >= 0) exec_close(io_pipe->pipe_fd[0]); + if (io_pipe->pipe_fd[1] >= 0) exec_close(io_pipe->pipe_fd[1]); break; } - } } return 0; - } +int setup_child_process(job_t *j, process_t *p, const io_chain_t &io_chain) { + bool ok = true; -int setup_child_process(job_t *j, process_t *p, const io_chain_t &io_chain) -{ - bool ok=true; - - if (p) - { + if (p) { ok = (0 == set_child_group(j, p, 1)); } - if (ok) - { + if (ok) { ok = (0 == handle_child_io(io_chain)); - if (p != 0 && ! ok) - { + if (p != 0 && !ok) { exit_without_destructors(1); } } - /* Set the handling for job control signals back to the default. */ - if (ok) - { + if (ok) { + // Set the handling for job control signals back to the default. signal_reset_handlers(); } - /* Remove all signal blocks */ - signal_unblock(); + signal_unblock(); // remove all signal blocks return ok ? 0 : -1; } int g_fork_count = 0; -/** - This function is a wrapper around fork. If the fork calls fails - with EAGAIN, it is retried FORK_LAPS times, with a very slight - delay between each lap. If fork fails even then, the process will - exit with an error message. -*/ -pid_t execute_fork(bool wait_for_threads_to_die) -{ +/// This function is a wrapper around fork. If the fork calls fails with EAGAIN, it is retried +/// FORK_LAPS times, with a very slight delay between each lap. If fork fails even then, the process +/// will exit with an error message. +pid_t execute_fork(bool wait_for_threads_to_die) { ASSERT_IS_MAIN_THREAD(); - if (wait_for_threads_to_die || JOIN_THREADS_BEFORE_FORK) - { - /* 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. */ + if (wait_for_threads_to_die || JOIN_THREADS_BEFORE_FORK) { + // 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(); } @@ -315,28 +253,22 @@ pid_t execute_fork(bool wait_for_threads_to_die) g_fork_count++; - for (i=0; i= 0) - { + if (pid >= 0) { return pid; } - if (errno != EAGAIN) - { + 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) - { + // 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); } } @@ -348,238 +280,210 @@ pid_t execute_fork(bool wait_for_threads_to_die) } #if FISH_USE_POSIX_SPAWN -bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, posix_spawn_file_actions_t *actions, job_t *j, process_t *p, const io_chain_t &io_chain) -{ - /* Initialize the output */ - if (posix_spawnattr_init(attr) != 0) - { +bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, + posix_spawn_file_actions_t *actions, job_t *j, process_t *p, + const io_chain_t &io_chain) { + // Initialize the output. + 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; } bool should_set_parent_group_id = false; int desired_parent_group_id = 0; - if (job_get_flag(j, JOB_CONTROL)) - { + if (job_get_flag(j, JOB_CONTROL)) { should_set_parent_group_id = true; - // PCA: I'm quite fuzzy on process groups, - // but I believe that the default value of 0 - // means that the process becomes its own - // group leader, which is what set_child_group did - // in this case. So we want this to be 0 if j->pgid is 0. + // PCA: I'm quite fuzzy on process groups, but I believe that the default value of 0 means + // that the process becomes its own group leader, which is what set_child_group did in this + // case. So we want this to be 0 if j->pgid is 0. desired_parent_group_id = j->pgid; } - /* Set the handling for job control signals back to the default. */ + // Set the handling for job control signals back to the default. bool reset_signal_handlers = true; - /* Remove all signal blocks */ + // Remove all signal blocks. bool reset_sigmask = true; - /* Set our flags */ + // Set our flags. short flags = 0; - if (reset_signal_handlers) - flags |= POSIX_SPAWN_SETSIGDEF; - if (reset_sigmask) - flags |= POSIX_SPAWN_SETSIGMASK; - if (should_set_parent_group_id) - flags |= POSIX_SPAWN_SETPGROUP; + if (reset_signal_handlers) flags |= POSIX_SPAWN_SETSIGDEF; + if (reset_sigmask) flags |= POSIX_SPAWN_SETSIGMASK; + if (should_set_parent_group_id) flags |= POSIX_SPAWN_SETPGROUP; int err = 0; - if (! err) - err = posix_spawnattr_setflags(attr, flags); + if (!err) err = posix_spawnattr_setflags(attr, flags); - if (! err && should_set_parent_group_id) + if (!err && should_set_parent_group_id) err = posix_spawnattr_setpgroup(attr, desired_parent_group_id); - /* Everybody gets default handlers */ - if (! err && reset_signal_handlers) - { + // Everybody gets default handlers. + if (!err && reset_signal_handlers) { sigset_t sigdefault; get_signals_with_handlers(&sigdefault); err = posix_spawnattr_setsigdefault(attr, &sigdefault); } - /* No signals blocked */ + // No signals blocked. sigset_t sigmask; sigemptyset(&sigmask); - if (! err && reset_sigmask) - err = posix_spawnattr_setsigmask(attr, &sigmask); - - for (size_t idx = 0; idx < io_chain.size(); idx++) - { + if (!err && reset_sigmask) err = posix_spawnattr_setsigmask(attr, &sigmask); + + for (size_t idx = 0; idx < io_chain.size(); idx++) { const shared_ptr io = io_chain.at(idx); - if (io->io_mode == IO_FD) - { + if (io->io_mode == IO_FD) { CAST_INIT(const io_fd_t *, io_fd, io.get()); - if (io->fd == io_fd->old_fd) - continue; + if (io->fd == io_fd->old_fd) continue; } - switch (io->io_mode) - { - case IO_CLOSE: - { - if (! err) - err = posix_spawn_file_actions_addclose(actions, io->fd); + switch (io->io_mode) { + case IO_CLOSE: { + if (!err) err = posix_spawn_file_actions_addclose(actions, io->fd); break; } - case IO_FILE: - { + case IO_FILE: { CAST_INIT(const io_file_t *, io_file, io.get()); - if (! err) - err = posix_spawn_file_actions_addopen(actions, io->fd, io_file->filename_cstr, io_file->flags /* mode */, OPEN_MASK); + if (!err) + err = posix_spawn_file_actions_addopen(actions, io->fd, io_file->filename_cstr, + io_file->flags /* mode */, OPEN_MASK); break; } - case IO_FD: - { + case IO_FD: { CAST_INIT(const io_fd_t *, io_fd, io.get()); - if (! err) - err = posix_spawn_file_actions_adddup2(actions, io_fd->old_fd /* from */, io->fd /* to */); + if (!err) + err = posix_spawn_file_actions_adddup2(actions, io_fd->old_fd /* from */, + io->fd /* to */); break; } case IO_BUFFER: - case IO_PIPE: - { + case IO_PIPE: { CAST_INIT(const io_pipe_t *, io_pipe, io.get()); unsigned int write_pipe_idx = (io_pipe->is_input ? 0 : 1); int from_fd = io_pipe->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_pipe->pipe_fd[0]); - if (! err) - err = posix_spawn_file_actions_addclose(actions, io_pipe->pipe_fd[1]); - } - else - { - if (! err) - err = posix_spawn_file_actions_addclose(actions, io_pipe->pipe_fd[0]); + 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_pipe->pipe_fd[0]); + if (!err) err = posix_spawn_file_actions_addclose(actions, io_pipe->pipe_fd[1]); + } else { + if (!err) err = posix_spawn_file_actions_addclose(actions, io_pipe->pipe_fd[0]); } break; } } } - /* Clean up on error */ - if (err) - { + // Clean up on error. + if (err) { posix_spawnattr_destroy(attr); posix_spawn_file_actions_destroy(actions); } - return ! err; + return !err; } -#endif //FISH_USE_POSIX_SPAWN +#endif // FISH_USE_POSIX_SPAWN -void safe_report_exec_error(int err, const char *actual_cmd, const char * const *argv, const char *const *envv) -{ +void safe_report_exec_error(int err, const char *actual_cmd, const char *const *argv, + const char *const *envv) { debug_safe(0, "Failed to execute process '%s'. Reason:", actual_cmd); - switch (err) - { - - case E2BIG: - { + switch (err) { + case E2BIG: { char sz1[128], sz2[128]; long arg_max = -1; size_t sz = 0; - const char * const *p; - for (p=argv; *p; p++) - { - sz += strlen(*p)+1; + const char *const *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); - if (arg_max > 0) - { + if (arg_max > 0) { format_size_safe(sz2, static_cast(arg_max)); - 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, + "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; } - case ENOEXEC: - { + case ENOEXEC: { const char *err = safe_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); + 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(). */ + 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 - { + 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: - { + case ENOMEM: { debug_safe(0, "Out of memory"); break; } - default: - { + default: { const char *err = safe_strerror(errno); debug_safe(0, "exec: %s", err); - // debug(0, L"The file '%ls' is marked as an executable but could not be run by the operating system.", p->actual_cmd); + // debug(0, L"The file '%ls' is marked as an executable but could not be run by the + // operating system.", p->actual_cmd); break; } } } -/** Perform output from builtins. May be called from a forked child, so don't do anything that may allocate memory, etc.. */ -bool do_builtin_io(const char *out, size_t outlen, const char *err, size_t errlen) -{ +/// Perform output from builtins. May be called from a forked child, so don't do anything that may +/// allocate memory, etc. +bool do_builtin_io(const char *out, size_t outlen, const char *err, size_t errlen) { bool success = true; - if (out && outlen) - { - if (write_loop(STDOUT_FILENO, out, outlen) < 0) - { + if (out && outlen) { + if (write_loop(STDOUT_FILENO, out, outlen) < 0) { int e = errno; debug_safe(0, "Error while writing to stdout"); safe_perror("write_loop"); @@ -588,10 +492,8 @@ bool do_builtin_io(const char *out, size_t outlen, const char *err, size_t errle } } - if (err && errlen) - { - if (write_loop(STDERR_FILENO, err, errlen) < 0) - { + if (err && errlen) { + if (write_loop(STDERR_FILENO, err, errlen) < 0) { success = false; } } diff --git a/src/postfork.h b/src/postfork.h index b2ef18ca0..ed441de39 100644 --- a/src/postfork.h +++ b/src/postfork.h @@ -1,7 +1,5 @@ -/** \file postfork.h - - Functions that we may safely call after fork(), of which there are very few. In particular we cannot allocate memory, since we're insane enough to call fork from a multithreaded process. -*/ +// Functions that we may safely call after fork(), of which there are very few. In particular we +// cannot allocate memory, since we're insane enough to call fork from a multithreaded process. #ifndef FISH_POSTFORK_H #define FISH_POSTFORK_H @@ -20,52 +18,48 @@ class io_chain_t; class job_t; class process_t; -/** - This function should be called by both the parent process and the - child right after fork() has been called. If job control is - enabled, the child is put in the jobs group, and if the child is - also in the foreground, it is also given control of the - terminal. When called in the parent process, this function may - fail, since the child might have already finished and called - exit. The parent process may safely ignore the exit status of this - call. - - Returns 0 on sucess, -1 on failiure. -*/ +/// This function should be called by both the parent process and the child right after fork() has +/// been called. If job control is enabled, the child is put in the jobs group, and if the child is +/// also in the foreground, it is also given control of the terminal. When called in the parent +/// process, this function may fail, since the child might have already finished and called exit. +/// The parent process may safely ignore the exit status of this call. +/// +/// Returns 0 on sucess, -1 on failiure. int set_child_group(job_t *j, process_t *p, int print_errors); -/** - Initialize a new child process. This should be called right away - after forking in the child process. If job control is enabled for - this job, the process is put in the process group of the job, all - signal handlers are reset, signals are unblocked (this function may - only be called inside the exec function, which blocks all signals), - and all IO redirections and other file descriptor actions are - performed. - - \param j the job to set up the IO for - \param p the child process to set up - \param io_chain the IO chain to use - - \return 0 on sucess, -1 on failiure. When this function returns, - signals are always unblocked. On failiure, signal handlers, io - redirections and process group of the process is undefined. -*/ +/// Initialize a new child process. This should be called right away after forking in the child +/// process. If job control is enabled for this job, the process is put in the process group of the +/// job, all signal handlers are reset, signals are unblocked (this function may only be called +/// inside the exec function, which blocks all signals), and all IO redirections and other file +/// descriptor actions are performed. +/// +/// \param j the job to set up the IO for +/// \param p the child process to set up +/// \param io_chain the IO chain to use +/// +/// \return 0 on sucess, -1 on failiure. When this function returns, 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, const io_chain_t &io_chain); -/* 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. -*/ +/// 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. pid_t execute_fork(bool wait_for_threads_to_die); -/* Perform output from builtins. Returns true on success. */ +/// Perform output from builtins. Returns true on success. bool do_builtin_io(const char *out, size_t outlen, const char *err, size_t errlen); -/** Report an error from failing to exec or posix_spawn a command */ -void safe_report_exec_error(int err, const char *actual_cmd, const char * const *argv, const char * const *envv); +/// Report an error from failing to exec or posix_spawn a command. +void safe_report_exec_error(int err, const char *actual_cmd, const char *const *argv, + const char *const *envv); #if FISH_USE_POSIX_SPAWN -/* Initializes and fills in a posix_spawnattr_t; on success, the caller should destroy it via posix_spawnattr_destroy */ -bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, posix_spawn_file_actions_t *actions, job_t *j, process_t *p, const io_chain_t &io_chain); +/// Initializes and fills in a posix_spawnattr_t; on success, the caller should destroy it via +/// posix_spawnattr_destroy. +bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, + posix_spawn_file_actions_t *actions, job_t *j, process_t *p, + const io_chain_t &io_chain); #endif #endif From 9d742a4fa18d3bdab0ab2b68f8f57e334ea3c0e1 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Mon, 2 May 2016 21:41:17 -0700 Subject: [PATCH 205/363] restyle proc module to match project style Reduces lint errors from 134 to 101 (-25%). Line count from 1994 to 1466 (-26%). Another step in resolving issue #2902. --- src/print_help.cpp | 25 +- src/print_help.h | 11 +- src/proc.cpp | 1208 +++++++++++++++++--------------------------- src/proc.h | 558 +++++++------------- 4 files changed, 637 insertions(+), 1165 deletions(-) diff --git a/src/print_help.cpp b/src/print_help.cpp index 856b87d56..ad479023e 100644 --- a/src/print_help.cpp +++ b/src/print_help.cpp @@ -1,33 +1,22 @@ -/** \file print_help.c - Print help message for the specified command -*/ -#include +// Print help message for the specified command. #include +#include #include -#include +#include "common.h" #include "print_help.h" #define CMD_LEN 1024 #define HELP_ERR "Could not show help message\n" -/* defined in common.h */ -ssize_t write_loop(int fd, const char *buff, size_t count); - - -void print_help(const char *c, int fd) -{ - char cmd[ CMD_LEN]; +void print_help(const char *c, int fd) { + char cmd[CMD_LEN]; int printed = snprintf(cmd, CMD_LEN, "fish -c '__fish_print_help %s >&%d'", c, fd); - if (printed < CMD_LEN) - { - if ((system(cmd) == -1)) - { + if (printed < CMD_LEN) { + if ((system(cmd) == -1)) { write_loop(2, HELP_ERR, strlen(HELP_ERR)); } - } - } diff --git a/src/print_help.h b/src/print_help.h index 005800b11..9c5a62298 100644 --- a/src/print_help.h +++ b/src/print_help.h @@ -1,15 +1,8 @@ - -/** \file print_help.h - Print help message for the specified command -*/ - +// Print help message for the specified command. #ifndef FISH_PRINT_HELP_H #define FISH_PRINT_HELP_H -/** - Print help message for the specified command -*/ - +/// Print help message for the specified command. void print_help(const char *cmd, int fd); #endif diff --git a/src/proc.cpp b/src/proc.cpp index f4e47cc91..9bec1811b 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -1,27 +1,22 @@ -/** \file proc.c - -Utilities for keeping track of jobs, processes and subshells, as -well as signal handling functions for tracking children. These -functions do not themselves launch new processes, the exec library -will call proc to create representations of the running jobs as -needed. - -Some of the code in this file is based on code from the Glibc manual. -*/ +// Utilities for keeping track of jobs, processes and subshells, as well as signal handling +// functions for tracking children. These functions do not themselves launch new processes, the exec +// library will call proc to create representations of the running jobs as needed. +// +// Some of the code in this file is based on code from the Glibc manual. // IWYU pragma: no_include <__bit_reference> #include "config.h" +#include +#include +#include #include #include -#include -#include #include -#include +#include +#include #include #include #include -#include -#include #if HAVE_TERM_H #include #elif HAVE_NCURSES_TERM_H @@ -33,62 +28,49 @@ Some of the code in this file is based on code from the Glibc manual. #ifdef HAVE_SYS_SELECT_H #include #endif -#include #include #include // IWYU pragma: keep +#include #include // IWYU pragma: keep -#include "fallback.h" // IWYU pragma: keep -#include "util.h" -#include "wutil.h" // IWYU pragma: keep -#include "proc.h" #include "common.h" +#include "event.h" +#include "fallback.h" // IWYU pragma: keep +#include "io.h" +#include "output.h" +#include "parse_tree.h" +#include "parser.h" +#include "proc.h" #include "reader.h" #include "sanity.h" -#include "parser.h" #include "signal.h" -#include "event.h" -#include "output.h" -#include "io.h" -#include "parse_tree.h" +#include "util.h" +#include "wutil.h" // IWYU pragma: keep -/** - Size of buffer for reading buffered output -*/ +/// Size of buffer for reading buffered output. #define BUFFER_SIZE 4096 -/** - Status of last process to exit -*/ -static int last_status=0; +/// Status of last process to exit. +static int last_status = 0; -bool job_list_is_empty(void) -{ +bool job_list_is_empty(void) { ASSERT_IS_MAIN_THREAD(); return parser_t::principal_parser().job_list().empty(); } -void job_iterator_t::reset() -{ +void job_iterator_t::reset() { this->current = job_list->begin(); this->end = job_list->end(); } -job_iterator_t::job_iterator_t(job_list_t &jobs) : job_list(&jobs) -{ - this->reset(); -} +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()) { ASSERT_IS_MAIN_THREAD(); this->reset(); } -size_t job_iterator_t::count() const -{ - return this->job_list->size(); -} +size_t job_iterator_t::count() const { return this->job_list->size(); } #if 0 // This isn't used so the lint tools were complaining about its presence. I'm keeping it in the @@ -106,193 +88,150 @@ void print_jobs(void) } #endif -int is_interactive_session=0; -int is_subshell=0; -int is_block=0; -int is_login=0; -int is_event=0; +int is_interactive_session = 0; +int is_subshell = 0; +int is_block = 0; +int is_login = 0; +int is_event = 0; pid_t proc_last_bg_pid = 0; int job_control_mode = JOB_CONTROL_INTERACTIVE; -int no_exec=0; +int no_exec = 0; 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(); - /* is_interactive is initialized to -1; ensure someone has popped/pushed it before then */ + // is_interactive is initialized to -1; ensure someone has popped/pushed it before then. assert(is_interactive >= 0); return is_interactive > 0; } -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; } -/** - The event variable used to send all process event -*/ +/// The event variable used to send all process event. static event_t event(0); -/** - A stack containing the values of is_interactive. Used by proc_push_interactive and proc_pop_interactive. -*/ +/// A stack containing the values of is_interactive. Used by proc_push_interactive and +/// proc_pop_interactive. static std::vector interactive_stack; -void proc_init() -{ - proc_push_interactive(0); -} +void proc_init() { proc_push_interactive(0); } - -/** - Remove job from list of jobs -*/ -static int job_remove(job_t *j) -{ +/// Remove job from list of jobs. +static int job_remove(job_t *j) { ASSERT_IS_MAIN_THREAD(); return parser_t::principal_parser().job_remove(j); } -void job_promote(job_t *job) -{ +void job_promote(job_t *job) { ASSERT_IS_MAIN_THREAD(); parser_t::principal_parser().job_promote(job); } - -/* - Remove job from the job list and free all memory associated with - it. -*/ -void job_free(job_t * j) -{ +/// Remove job from the job list and free all memory associated with it. +void job_free(job_t *j) { job_remove(j); delete j; } -void proc_destroy() -{ +void proc_destroy() { job_list_t &jobs = parser_t::principal_parser().job_list(); - while (! jobs.empty()) - { + 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) { ASSERT_IS_MAIN_THREAD(); last_status = s; } -int proc_get_last_status() -{ - return last_status; -} +int proc_get_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. */ +// 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. static pthread_mutex_t job_id_lock = PTHREAD_MUTEX_INITIALIZER; static std::vector consumed_job_ids; -job_id_t acquire_job_id(void) -{ +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()) - { - /* We found a slot. Note that slot 0 corresponds to job ID 1. */ + // 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()) { + // We found a slot. Note that slot 0 corresponds to job ID 1. *slot = true; return (job_id_t)(slot - consumed_job_ids.begin() + 1); - } - else - { - /* We did not find a slot; create a new slot. The size of the vector is now the job ID (since it is one larger than the slot). */ + } else { + // We did not find a slot; create a new slot. The size of the vector is now the job ID + // (since it is one larger than the slot). consumed_job_ids.push_back(true); return (job_id_t)consumed_job_ids.size(); } } -void release_job_id(job_id_t jid) -{ +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 */ + // 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 */ + // Clear it and then resize the vector to eliminate unused trailing job IDs. consumed_job_ids.at(slot) = false; - while (count--) - { - if (consumed_job_ids.at(count)) - break; + 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. - - \param j the job to test -*/ -int job_is_stopped(const job_t *j) -{ +/// Return true if all processes in the job have stopped or completed. +/// +/// \param j the job to test +int job_is_stopped(const job_t *j) { process_t *p; - for (p = j->first_process; p; p = p->next) - { - if (!p->completed && !p->stopped) - { + 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. - - \param j the job to test -*/ -bool job_is_completed(const job_t *j) -{ +/// Return true if the last processes in the job has completed. +/// +/// \param j the job to test +bool job_is_completed(const job_t *j) { assert(j->first_process != NULL); bool result = true; - for (process_t *p = j->first_process; p != NULL; p = p->next) - { - if (! p->completed) - { + for (process_t *p = j->first_process; p != NULL; p = p->next) { + if (!p->completed) { result = false; break; } @@ -300,114 +239,81 @@ bool job_is_completed(const job_t *j) return result; } -void job_set_flag(job_t *j, unsigned int flag, int set) -{ - if (set) - { +void job_set_flag(job_t *j, unsigned int flag, int set) { + if (set) { j->flags |= flag; - } - else - { + } else { j->flags &= ~flag; } } -int job_get_flag(const job_t *j, unsigned int flag) -{ - return !!(j->flags & flag); -} +int job_get_flag(const job_t *j, unsigned int flag) { return !!(j->flags & flag); } -int job_signal(job_t *j, int signal) -{ +int job_signal(job_t *j, int signal) { pid_t my_pid = getpid(); int res = 0; - if (j->pgid != my_pid) - { + if (j->pgid != my_pid) { res = killpg(j->pgid, SIGHUP); - } - else - { - for (process_t *p = j->first_process; p; p=p->next) - { - if (! p->completed) - { - if (p->pid) - { - if (kill(p->pid, SIGHUP)) - { + } else { + for (process_t *p = j->first_process; p; p = p->next) { + if (!p->completed) { + if (p->pid) { + if (kill(p->pid, SIGHUP)) { res = -1; break; } } } } - } return res; } - -/** - Store the status of the process pid that was returned by waitpid. -*/ -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")) ); +/// Store the status of the process pid that was returned by waitpid. +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)) - { + if (WIFSTOPPED(status)) { p->stopped = 1; - } - else if (WIFSIGNALED(status) || WIFEXITED(status)) - { + } else if (WIFSIGNALED(status) || WIFEXITED(status)) { p->completed = 1; - } - else - { - /* This should never be reached */ + } else { + // This should never be reached. p->completed = 1; fprintf(stderr, "Process %ld exited abnormally\n", (long)p->pid); } } -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. */ - for (process_t *cursor = p; cursor != NULL; cursor = cursor->next) - { +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. + for (process_t *cursor = p; cursor != NULL; cursor = cursor->next) { cursor->completed = 1; } } -/** - Handle status update for child \c pid. - - \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) -{ +/// Handle status update for child \c pid. +/// +/// \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) { bool found_proc = false; const job_t *j = NULL; process_t *p = NULL; 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) - { + while (!found_proc && (j = jobs.next())) { + process_t *prev = 0; + for (p = j->first_process; p; p = p->next) { + if (pid == p->pid) { mark_process_status(j, p, status); - if (p->completed && prev != 0) - { - if (!prev->completed && prev->pid) - { - kill(prev->pid,SIGPIPE); + if (p->completed && prev != 0) { + if (!prev->completed && prev->pid) { + kill(prev->pid, SIGPIPE); } } found_proc = true; @@ -417,90 +323,66 @@ static void handle_child_status(pid_t pid, int status) } } - - if (WIFSIGNALED(status) && - (WTERMSIG(status)==SIGINT || - WTERMSIG(status)==SIGQUIT)) - { - if (!is_interactive_session) - { + 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; + 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) - { + } 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) { 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... - */ + 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... } return; } -process_t::process_t() : - type(), // gets set later - internal_block_node(NODE_OFFSET_INVALID), - pid(0), - pipe_write_fd(0), - pipe_read_fd(0), - completed(0), - stopped(0), - status(0), - count_help_magic(0), - next(NULL) +process_t::process_t() + : type(), // gets set later + internal_block_node(NODE_OFFSET_INVALID), + pid(0), + pipe_write_fd(0), + pipe_read_fd(0), + completed(0), + stopped(0), + status(0), + count_help_magic(0), + next(NULL) #ifdef HAVE__PROC_SELF_STAT - ,last_time(), - last_jiffies(0) + , + last_time(), + last_jiffies(0) #endif { } -process_t::~process_t() -{ - if (this->next != NULL) - delete this->next; +process_t::~process_t() { + if (this->next != NULL) delete this->next; } -job_t::job_t(job_id_t jobid, const io_chain_t &bio) : - block_io(bio), - first_process(NULL), - pgid(0), - tmodes(), - job_id(jobid), - flags(0) -{ -} +job_t::job_t(job_id_t jobid, const io_chain_t &bio) + : block_io(bio), first_process(NULL), pgid(0), tmodes(), job_id(jobid), flags(0) {} -job_t::~job_t() -{ - if (first_process != NULL) - delete first_process; +job_t::~job_t() { + if (first_process != NULL) delete first_process; release_job_id(job_id); } -/* Return all the IO redirections. Start with the block IO, then walk over the processes */ -io_chain_t job_t::all_io_redirections() const -{ +/// Return all the IO redirections. Start with the block IO, then walk over the processes. +io_chain_t job_t::all_io_redirections() const { io_chain_t result = this->block_io; - for (process_t *p = this->first_process; p != NULL; p = p->next) - { + for (process_t *p = this->first_process; p != NULL; p = p->next) { result.append(p->io_chain()); } return result; @@ -508,136 +390,124 @@ io_chain_t job_t::all_io_redirections() const typedef unsigned int process_generation_count_t; -/* A static value tracking how many SIGCHLDs we have seen. This is only ever modified from within the SIGCHLD signal handler, and therefore does not need atomics or locks */ +/// A static value tracking how many SIGCHLDs we have seen. This is only ever modified from within +/// the SIGCHLD signal handler, and therefore does not need atomics or locks. static volatile process_generation_count_t s_sigchld_generation_count = 0; -/* If we have received a SIGCHLD signal, process any children. If await is false, this returns immediately if no SIGCHLD has been received. If await is true, this waits for one. Returns true if something was processed. This returns the number of children processed, or -1 on error. */ -static int process_mark_finished_children(bool wants_await) -{ +/// If we have received a SIGCHLD signal, process any children. If await is false, this returns +/// immediately if no SIGCHLD has been received. If await is true, this waits for one. Returns true +/// if something was processed. This returns the number of children processed, or -1 on error. +static int process_mark_finished_children(bool wants_await) { ASSERT_IS_MAIN_THREAD(); - - /* A static value tracking the SIGCHLD gen count at the time we last processed it. When this is different from s_sigchld_generation_count, it indicates there may be unreaped processes. There may not be if we reaped them via the other waitpid path. This is only ever modified from the main thread, and not from a signal handler. */ + + // A static value tracking the SIGCHLD gen count at the time we last processed it. When this is + // different from s_sigchld_generation_count, it indicates there may be unreaped processes. + // There may not be if we reaped them via the other waitpid path. This is only ever modified + // from the main thread, and not from a signal handler. static process_generation_count_t s_last_processed_sigchld_generation_count = 0; - + int processed_count = 0; bool got_error = false; - /* The critical read. This fetches a value which is only written in the signal handler. This needs to be an atomic read (we'd use sig_atomic_t, if we knew that were unsigned - fortunately aligned unsigned int is atomic on pretty much any modern chip.) It also needs to occur before we start reaping, since the signal handler can be invoked at any point. */ + // The critical read. This fetches a value which is only written in the signal handler. This + // needs to be an atomic read (we'd use sig_atomic_t, if we knew that were unsigned - + // fortunately aligned unsigned int is atomic on pretty much any modern chip.) It also needs to + // occur before we start reaping, since the signal handler can be invoked at any point. const process_generation_count_t local_count = s_sigchld_generation_count; - - /* Determine whether we have children to process. Note that we can't reliably use the difference because a single SIGCHLD may be delivered for multiple children - see #1768. Also if we are awaiting, we always process. */ + + // Determine whether we have children to process. Note that we can't reliably use the difference + // because a single SIGCHLD may be delivered for multiple children - see #1768. Also if we are + // awaiting, we always process. bool wants_waitpid = wants_await || local_count != s_last_processed_sigchld_generation_count; - if (wants_waitpid) - { - for (;;) - { - /* Call waitpid until we get 0/ECHILD. If we wait, it's only on the first iteration. So we want to set NOHANG (don't wait) unless wants_await is true and this is the first iteration. */ + if (wants_waitpid) { + for (;;) { + // Call waitpid until we get 0/ECHILD. If we wait, it's only on the first iteration. So + // we want to set NOHANG (don't wait) unless wants_await is true and this is the first + // iteration. int options = WUNTRACED; - if (! (wants_await && processed_count == 0)) - { + if (!(wants_await && processed_count == 0)) { options |= WNOHANG; } - + int status = -1; pid_t pid = waitpid(-1, &status, options); - if (pid > 0) - { - /* We got a valid pid */ + if (pid > 0) { + // We got a valid pid. handle_child_status(pid, status); processed_count += 1; - } - else if (pid == 0) - { - /* No ready-and-waiting children, we're done */ + } else if (pid == 0) { + // No ready-and-waiting children, we're done. break; - } - else - { - /* This indicates an error. One likely failure is ECHILD (no children), which we break on, and is not considered an error. The other likely failure is EINTR, which means we got a signal, which is considered an error. */ + } else { + // This indicates an error. One likely failure is ECHILD (no children), which we + // break on, and is not considered an error. The other likely failure is EINTR, + // which means we got a signal, which is considered an error. got_error = (errno != ECHILD); break; } } } - if (got_error) - { + if (got_error) { return -1; - } - else - { + } else { s_last_processed_sigchld_generation_count = local_count; return processed_count; } } - -/* This is called from a signal handler. The signal is always SIGCHLD. */ -void job_handle_signal(int signal, siginfo_t *info, void *con) -{ - /* This is the only place that this generation count is modified. It's OK if it overflows. */ +/// This is called from a signal handler. The signal is always SIGCHLD. +void job_handle_signal(int signal, siginfo_t *info, void *con) { + // This is the only place that this generation count is modified. It's OK if it overflows. s_sigchld_generation_count += 1; } -/* Given a command like "cat file", truncate it to a reasonable length */ -static wcstring truncate_command(const wcstring &cmd) -{ +/// Given a command like "cat file", truncate it to a reasonable length. +static wcstring truncate_command(const wcstring &cmd) { const size_t max_len = 32; - if (cmd.size() <= max_len) - { - // No truncation necessary + if (cmd.size() <= max_len) { + // No truncation necessary. return cmd; } - - // Truncation required + + // Truncation required. const bool ellipsis_is_unicode = (ellipsis_char == L'\x2026'); const size_t ellipsis_length = ellipsis_is_unicode ? 1 : 3; size_t trunc_length = max_len - ellipsis_length; - // Eat trailing whitespace - while (trunc_length > 0 && iswspace(cmd.at(trunc_length - 1))) - { + // Eat trailing whitespace. + while (trunc_length > 0 && iswspace(cmd.at(trunc_length - 1))) { trunc_length -= 1; } wcstring result = wcstring(cmd, 0, trunc_length); - // Append ellipsis - if (ellipsis_is_unicode) - { + // Append ellipsis. + if (ellipsis_is_unicode) { result.push_back(ellipsis_char); - } - else - { + } else { result.append(L"..."); } return result; } -/** - 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, size_t job_count) -{ +/// 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, size_t job_count) { fwprintf(stdout, L"\r"); - if (job_count == 1) - { + if (job_count == 1) { fwprintf(stdout, _(L"\'%ls\' has %ls"), truncate_command(j->command()).c_str(), status); - } - else - { - fwprintf(stdout, _(L"Job %d, \'%ls\' has %ls"), j->job_id, truncate_command(j->command()).c_str(), status); + } else { + fwprintf(stdout, _(L"Job %d, \'%ls\' has %ls"), j->job_id, + truncate_command(j->command()).c_str(), status); } fflush(stdout); - tputs(clr_eol,1,&writeb); + tputs(clr_eol, 1, &writeb); fwprintf(stdout, L"\n"); } -void proc_fire_event(const wchar_t *msg, int type, pid_t pid, int status) -{ - - event.type=type; +void proc_fire_event(const wchar_t *msg, int type, pid_t pid, int status) { + event.type = type; event.param1.pid = pid; event.arguments.push_back(msg); @@ -647,146 +517,126 @@ void proc_fire_event(const wchar_t *msg, int type, pid_t pid, int status) event.arguments.resize(0); } -int job_reap(bool interactive) -{ +int job_reap(bool interactive) { ASSERT_IS_MAIN_THREAD(); job_t *jnext; - int found=0; + int found = 0; - /* job_reap may fire an event handler, we do not want to call ourselves recursively (to avoid infinite recursion). */ + // job_reap may fire an event handler, we do not want to call ourselves recursively (to avoid + // infinite recursion). static bool locked = false; - if (locked) - { + if (locked) { return 0; } locked = true; - + process_mark_finished_children(false); - /* Preserve the exit status */ + // Preserve the exit status. const int saved_status = proc_get_last_status(); job_iterator_t jobs; const size_t job_count = jobs.count(); jnext = jobs.next(); - while (jnext) - { + while (jnext) { job_t *j = jnext; jnext = jobs.next(); - /* - 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))) - { + // 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 (process_t *p = j->first_process; p; p=p->next) - { + for (process_t *p = j->first_process; p; p = p->next) { int s; - if (!p->completed) - continue; + if (!p->completed) continue; - if (!p->pid) - continue; + if (!p->pid) continue; s = p->status; - proc_fire_event(L"PROCESS_EXIT", EVENT_EXIT, p->pid, (WIFSIGNALED(s)?-1:WEXITSTATUS(s))); + 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)) - { - /* Print nothing if we get SIGINT in the foreground process group, to avoid spamming obvious stuff on the console (#1119). If we get SIGINT for the foreground process, assume the user typed ^C and can see it working. It's possible they didn't, and the signal was delivered via pkill, etc., but the SIGINT/SIGTERM distinction is precisely to allow INT to be from a UI and TERM to be programmatic, so this assumption is keeping with the design of signals. - If echoctl is on, then the terminal will have written ^C to the console. If off, it won't have. We don't echo ^C either way, so as to respect the user's preference. */ - if (WTERMSIG(p->status) != SIGINT || ! job_get_flag(j, JOB_FOREGROUND)) - { - if (proc_is_job) - { - // We want to report the job number, unless it's the only job, in which case we don't need to - const wcstring job_number_desc = (job_count == 1) ? wcstring() : format_string(L"Job %d, ", j->job_id); + 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)) { + // Print nothing if we get SIGINT in the foreground process group, to avoid + // spamming obvious stuff on the console (#1119). If we get SIGINT for the + // foreground process, assume the user typed ^C and can see it working. It's + // possible they didn't, and the signal was delivered via pkill, etc., but + // the SIGINT/SIGTERM distinction is precisely to allow INT to be from a UI + // and TERM to be programmatic, so this assumption is keeping with the + // design of signals. If echoctl is on, then the terminal will have written + // ^C to the console. If off, it won't have. We don't echo ^C either way, so + // as to respect the user's preference. + if (WTERMSIG(p->status) != SIGINT || !job_get_flag(j, JOB_FOREGROUND)) { + if (proc_is_job) { + // We want to report the job number, unless it's the only job, in + // which case we don't need to. + const wcstring job_number_desc = + (job_count == 1) ? wcstring() + : format_string(L"Job %d, ", j->job_id); fwprintf(stdout, _(L"%ls: %ls\'%ls\' terminated by signal %ls (%ls)"), - program_name, - job_number_desc.c_str(), + program_name, job_number_desc.c_str(), + truncate_command(j->command()).c_str(), + sig2wcs(WTERMSIG(p->status)), + signal_get_desc(WTERMSIG(p->status))); + } else { + const wcstring job_number_desc = + (job_count == 1) ? wcstring() + : format_string(L"from job %d, ", j->job_id); + fwprintf(stdout, _(L"%ls: Process %d, \'%ls\' %ls\'%ls\' " + L"terminated by signal %ls (%ls)"), + program_name, p->pid, p->argv0(), job_number_desc.c_str(), truncate_command(j->command()).c_str(), sig2wcs(WTERMSIG(p->status)), signal_get_desc(WTERMSIG(p->status))); } - else - { - const wcstring job_number_desc = (job_count == 1) ? wcstring() : format_string(L"from job %d, ", j->job_id); - fwprintf(stdout, - _(L"%ls: Process %d, \'%ls\' %ls\'%ls\' terminated by signal %ls (%ls)"), - program_name, - p->pid, - p->argv0(), - job_number_desc.c_str(), - truncate_command(j->command()).c_str(), - sig2wcs(WTERMSIG(p->status)), - signal_get_desc(WTERMSIG(p->status))); - } - tputs(clr_eol,1,&writeb); + tputs(clr_eol, 1, &writeb); fwprintf(stdout, L"\n"); } - found=1; + found = 1; } - /* - Clear status so it is not reported more than once - */ + // 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)) - { + // 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"), job_count); - found=1; + 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)) - { + } 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"), job_count); - found=1; + found = 1; } job_set_flag(j, JOB_NOTIFIED, 1); } } - if (found) - fflush(stdout); + if (found) fflush(stdout); - /* Restore the exit status. */ + // Restore the exit status. proc_set_last_status(saved_status); locked = false; @@ -794,160 +644,119 @@ int job_reap(bool interactive) return found; } - #ifdef HAVE__PROC_SELF_STAT -/** - Maximum length of a /proc/[PID]/stat filename -*/ +/// Maximum length of a /proc/[PID]/stat filename. #define FN_SIZE 256 -/** - Get the CPU time for the specified process -*/ -unsigned long proc_get_jiffies(process_t *p) -{ +/// Get the CPU time for the specified process. +unsigned long proc_get_jiffies(process_t *p) { wchar_t fn[FN_SIZE]; char state; - int pid, ppid, pgrp, - session, tty_nr, tpgid, - exit_signal, processor; + 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; + 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; + 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; + if (!f) return 0; - int count = fscanf(f, - "%d %s %c " - "%d %d %d " - "%d %d %lu " + int count = fscanf( + f, + "%d %s %c " + "%d %d %d " + "%d %d %lu " - "%lu %lu %lu " - "%lu %lu %lu " - "%ld %ld %ld " + "%lu %lu %lu " + "%lu %lu %lu " + "%ld %ld %ld " - "%ld %ld %ld " - "%lu %lu %ld " - "%lu %lu %lu " + "%ld %ld %ld " + "%lu %lu %ld " + "%lu %lu %lu " - "%lu %lu %lu " - "%lu %lu %lu " - "%lu %lu %lu " + "%lu %lu %lu " + "%lu %lu %lu " + "%lu %lu %lu " - "%lu %d %d ", + "%lu %d %d ", - &pid, comm, &state, - &ppid, &pgrp, &session, - &tty_nr, &tpgid, &flags, + &pid, comm, &state, &ppid, &pgrp, &session, &tty_nr, &tpgid, &flags, - &minflt, &cminflt, &majflt, - &cmajflt, &utime, &stime, - &cutime, &cstime, &priority, + &minflt, &cminflt, &majflt, &cmajflt, &utime, &stime, &cutime, &cstime, &priority, - &nice, &placeholder, &itrealvalue, - &starttime, &vsize, &rss, - &rlim, &startcode, &endcode, + &nice, &placeholder, &itrealvalue, &starttime, &vsize, &rss, &rlim, &startcode, &endcode, - &startstack, &kstkesp, &kstkeip, - &signal, &blocked, &sigignore, - &sigcatch, &wchan, &nswap, + &startstack, &kstkesp, &kstkeip, &signal, &blocked, &sigignore, &sigcatch, &wchan, &nswap, - &cnswap, &exit_signal, &processor - ); + &cnswap, &exit_signal, &processor); - /* - Don't need to check exit status of fclose on read-only streams - */ + // Don't need to check exit status of fclose on read-only streams. fclose(f); - - if (count < 17) - { + + if (count < 17) { return 0; } - return utime+stime+cutime+cstime; - + return utime + stime + cutime + cstime; } -/** - Update the CPU time for all jobs -*/ -void proc_update_jiffies() -{ - job_t* job; +/// Update the CPU time for all jobs. +void proc_update_jiffies() { + 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) - { + 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); } } } - #endif -/** - Check if there are buffers associated with the job, and select on - them for a while if available. - - \param j the job to test - - \return 1 if buffers were available, zero otherwise -*/ -static int select_try(job_t *j) -{ +/// Check if there are buffers associated with the job, and select on them for a while if available. +/// +/// \param j the job to test +/// +/// \return 1 if buffers were available, zero otherwise +static int select_try(job_t *j) { fd_set fds; - int maxfd=-1; + int maxfd = -1; FD_ZERO(&fds); const io_chain_t chain = j->all_io_redirections(); - for (size_t idx = 0; idx < chain.size(); idx++) - { + for (size_t idx = 0; idx < chain.size(); idx++) { const io_data_t *io = chain.at(idx).get(); - if (io->io_mode == IO_BUFFER) - { + if (io->io_mode == IO_BUFFER) { CAST_INIT(const io_pipe_t *, io_pipe, io); int fd = io_pipe->pipe_fd[0]; -// fwprintf( stderr, L"fd %d on job %ls\n", fd, j->command ); + // 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) - { + if (maxfd >= 0) { int retval; struct timeval tv; - tv.tv_sec=0; - tv.tv_usec=10000; + tv.tv_sec = 0; + tv.tv_usec = 10000; - retval =select(maxfd+1, &fds, 0, 0, &tv); + retval = select(maxfd + 1, &fds, 0, 0, &tv); if (retval == 0) { debug(3, L"select_try hit timeout\n"); } @@ -957,90 +766,58 @@ static int select_try(job_t *j) return -1; } -/** - Read from descriptors until they are empty. - - \param j the job to test -*/ -static void read_try(job_t *j) -{ +/// Read from descriptors until they are empty. +/// +/// \param j the job to test +static void read_try(job_t *j) { io_buffer_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. const io_chain_t chain = j->all_io_redirections(); - for (size_t idx = 0; idx < chain.size(); idx++) - { + for (size_t idx = 0; idx < chain.size(); idx++) { io_data_t *d = chain.at(idx).get(); - if (d->io_mode == IO_BUFFER) - { + if (d->io_mode == IO_BUFFER) { buff = static_cast(d); } } - if (buff) - { + if (buff) { debug(3, L"proc::read_try('%ls')\n", j->command_wcstr()); - while (1) - { + while (1) { char b[BUFFER_SIZE]; long l; - l=read_blocked(buff->pipe_fd[0], - b, BUFFER_SIZE); - if (l==0) - { + l = read_blocked(buff->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")); + } 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 - { + } else { buff->out_buffer_append(b, l); } } } } - -/** - Give ownership of the terminal to the specified job. - - \param j The job to give the terminal to. - - \param cont If this variable is set, we are giving back control to - a job that has previously been stopped. In that case, we need to - set the terminal attributes to those saved in the job. - */ -static bool 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()); +/// Give ownership of the terminal to the specified job. +/// +/// \param j The job to give the terminal to. +/// \param cont If this variable is set, we are giving back control to a job that has previously +/// been stopped. In that case, we need to set the terminal attributes to those saved in the job. +static bool 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 false; } - if (cont) - { - if (tcsetattr(0, TCSADRAIN, &j->tmodes)) - { - debug(1, - _(L"Could not send job %d ('%ls') to foreground"), - j->job_id, + 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 false; @@ -1049,16 +826,10 @@ static bool terminal_give_to_job(job_t *j, int cont) return true; } -/** - Returns control 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 . -*/ -static int terminal_return_from_job(job_t *j) -{ - - if (tcsetpgrp(0, getpgrp())) - { +/// Returns control 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. +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; @@ -1067,23 +838,19 @@ static int terminal_return_from_job(job_t *j) /* Save jobs terminal modes. */ - if (tcgetattr(0, &j->tmodes)) - { + 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 - */ +// 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)) - { + // 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; @@ -1093,29 +860,21 @@ static int terminal_return_from_job(job_t *j) return 1; } -void job_continue(job_t *j, bool cont) -{ - /* - Put job first in the job list - */ +void job_continue(job_t *j, bool cont) { + // Put job first in the job list. job_promote(j); 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"); + 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. Hack: ensure that stdin is marked as blocking first (#176). */ + if (!job_is_completed(j)) { + if (job_get_flag(j, JOB_TERMINAL) && job_get_flag(j, JOB_FOREGROUND)) { + // Put the job into the foreground. Hack: ensure that stdin is marked as blocking first + // (issue #176). make_fd_blocking(STDIN_FILENO); signal_block(); @@ -1124,34 +883,23 @@ void job_continue(job_t *j, bool cont) signal_unblock(); - if (!ok) - return; + if (!ok) return; } - /* - Send the job a continue signal, if necessary. - */ - if (cont) - { + // 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; + for (p = j->first_process; p; p = p->next) p->stopped = 0; - if (job_get_flag(j, JOB_CONTROL)) - { - if (killpg(j->pgid, SIGCONT)) - { + 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) - { + } else { + for (p = j->first_process; p; p = p->next) { + if (kill(p->pid, SIGCONT) < 0) { wperror(L"kill (SIGCONT)"); return; } @@ -1159,46 +907,33 @@ void job_continue(job_t *j, bool cont) } } - if (job_get_flag(j, JOB_FOREGROUND)) - { - /* Look for finished processes first, to avoid select() if it's already done. */ + if (job_get_flag(j, JOB_FOREGROUND)) { + // Look for finished processes first, to avoid select() if it's already done. process_mark_finished_children(false); - /* - Wait for job to report. - */ - while (! reader_exit_forced() && ! job_is_stopped(j) && ! job_is_completed(j)) - { -// debug( 1, L"select_try()" ); - switch (select_try(j)) - { - case 1: - { + // Wait for job to report. + while (!reader_exit_forced() && !job_is_stopped(j) && !job_is_completed(j)) { + // debug( 1, L"select_try()" ); + switch (select_try(j)) { + case 1: { read_try(j); process_mark_finished_children(false); break; } - - case 0: - { - /* No FDs are ready. Look for finished processes. */ + + case 0: { + // No FDs are ready. Look for finished processes. process_mark_finished_children(false); 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. - - This will return early if we get a signal, - like SIGHUP. - */ + 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. + // + // This will return early if we get a signal, like SIGHUP. process_mark_finished_children(true); break; } @@ -1207,40 +942,34 @@ void job_continue(job_t *j, bool cont) } } - 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. + 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! + // 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; + 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 circuited builtin - */ - if (p->pid) - { + 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 circuited 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); + // 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)) - { + // Put the shell back in the foreground. + if (job_get_flag(j, JOB_TERMINAL) && job_get_flag(j, JOB_FOREGROUND)) { int ok; signal_block(); @@ -1249,114 +978,79 @@ void job_continue(job_t *j, bool cont) signal_unblock(); - if (!ok) - return; - + if (!ok) return; } } - } -int proc_format_status(int status) -{ - if (WIFSIGNALED(status)) - { - return 128+WTERMSIG(status); - } - else if (WIFEXITED(status)) - { +int proc_format_status(int status) { + if (WIFSIGNALED(status)) { + return 128 + WTERMSIG(status); + } else if (WIFEXITED(status)) { return WEXITSTATUS(status); } return status; - } - -void proc_sanity_check() -{ +void proc_sanity_check() { job_t *j; - job_t *fg_job=0; + job_t *fg_job = 0; job_iterator_t jobs; - while ((j = jobs.next())) - { + while ((j = jobs.next())) { process_t *p; - if (!job_get_flag(j, JOB_CONSTRUCTED)) - continue; + if (!job_get_flag(j, JOB_CONSTRUCTED)) continue; + validate_pointer(j->first_process, _(L"Process list pointer"), 0); - 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()); + // 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) - { - /* Internal block nodes do not have argv - see #1545 */ + while (p) { + // Internal block nodes do not have argv - see issue #1545. bool null_ok = (p->type == INTERNAL_BLOCK_NODE); validate_pointer(p->get_argv(), _(L"Process argument list"), null_ok); validate_pointer(p->argv0(), _(L"Process name"), null_ok); validate_pointer(p->next, _(L"Process list pointer"), true); - if ((p->stopped & (~0x00000001)) != 0) - { - debug(0, - _(L"Job '%ls', process '%ls' has inconsistent state \'stopped\'=%d"), - j->command_wcstr(), - p->argv0(), - p->stopped); + 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); + 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; + p = p->next; } - } } -void proc_push_interactive(int value) -{ +void proc_push_interactive(int value) { ASSERT_IS_MAIN_THREAD(); int old = is_interactive; interactive_stack.push_back(is_interactive); is_interactive = value; - if (old != value) - signal_set_handlers(); + if (old != value) signal_set_handlers(); } -void proc_pop_interactive() -{ +void proc_pop_interactive() { ASSERT_IS_MAIN_THREAD(); int old = is_interactive; - is_interactive= interactive_stack.back(); + 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/src/proc.h b/src/proc.h index 7fcbe6dad..3d24405ca 100644 --- a/src/proc.h +++ b/src/proc.h @@ -1,254 +1,170 @@ -/** \file proc.h - - Prototypes for utilities for keeping track of jobs, processes and subshells, as - well as signal handling functions for tracking children. These - functions do not themselves launch new processes, the exec library - will call proc to create representations of the running jobs as - needed. - -*/ +// Prototypes for utilities for keeping track of jobs, processes and subshells, as well as signal +// handling functions for tracking children. These functions do not themselves launch new processes, +// the exec library will call proc to create representations of the running jobs as needed. #ifndef FISH_PROC_H #define FISH_PROC_H #include "config.h" // IWYU pragma: keep -#include -#include #include +#include +#include #include +#include // IWYU pragma: keep #include #include -#include -#include // IWYU pragma: keep +#include -#include "io.h" #include "common.h" +#include "io.h" #include "parse_tree.h" -/** - The status code use when a command was not found -*/ +/// The status code use when a command was not found. #define STATUS_UNKNOWN_COMMAND 127 -/** - The status code use when an unknown error occured during execution of a command -*/ +/// The status code use when an unknown error occured during execution of a command. #define STATUS_NOT_EXECUTABLE 126 -/** - The status code use when an unknown error occured during execution of a command -*/ +/// The status code use when an unknown error occured during execution of a command. #define STATUS_EXEC_FAIL 125 -/** - The status code use when a wildcard had no matches -*/ +/// The status code use when a wildcard had no matches. #define STATUS_UNMATCHED_WILDCARD 124 -/** - The status code used for normal exit in a builtin -*/ +/// The status code used for normal exit in a builtin. #define STATUS_BUILTIN_OK 0 -/** - The status code used for erroneous argument combinations in a builtin -*/ +/// The status code used for erroneous argument combinations in a builtin. #define STATUS_BUILTIN_ERROR 1 -/** - Types of processes -*/ -enum process_type_t -{ - /** - A regular external command - */ +/// Types of processes. +enum process_type_t { + /// A regular external command. EXTERNAL, - /** - A builtin command - */ + /// A builtin command. INTERNAL_BUILTIN, - /** - A shellscript function - */ + /// A shellscript function. INTERNAL_FUNCTION, - - /** A block of commands, represented as a node */ + /// A block of commands, represented as a node. INTERNAL_BLOCK_NODE, - - /** - The exec builtin - */ + /// The exec builtin. INTERNAL_EXEC }; -enum -{ +enum { JOB_CONTROL_ALL, JOB_CONTROL_INTERACTIVE, JOB_CONTROL_NONE, -} -; - -/** - A structure representing a single fish process. Contains variables - for tracking process state and the process argument - list. Actually, a fish process can be either a regular external - process, an internal builtin which may or may not spawn a fake IO - process during execution, a shellscript function or a block of - commands to be evaluated by calling eval. Lastly, this process can - be the result of an exec command. The role of this process_t is - determined by the type field, which can be one of EXTERNAL, - INTERNAL_BUILTIN, INTERNAL_FUNCTION, INTERNAL_EXEC. - - The process_t contains information on how the process should be - started, such as command name and arguments, as well as runtime - information on the status of the actual physical process which - represents it. Shellscript functions, builtins and blocks of code - may all need to spawn an external process that handles the piping - and redirecting of IO for them. - - If the process is of type EXTERNAL or INTERNAL_EXEC, argv is the - argument array and actual_cmd is the absolute path of the command - to execute. - - If the process is of type INTERNAL_BUILTIN, argv is the argument - vector, and argv[0] is the name of the builtin command. - - If the process is of type INTERNAL_FUNCTION, argv is the argument - vector, and argv[0] is the name of the shellscript function. - -*/ -class process_t -{ -private: +}; +/// A structure representing a single fish process. Contains variables for tracking process state +/// and the process argument list. Actually, a fish process can be either a regular external +/// process, an internal builtin which may or may not spawn a fake IO process during execution, a +/// shellscript function or a block of commands to be evaluated by calling eval. Lastly, this +/// process can be the result of an exec command. The role of this process_t is determined by the +/// type field, which can be one of EXTERNAL, INTERNAL_BUILTIN, INTERNAL_FUNCTION, INTERNAL_EXEC. +/// +/// The process_t contains information on how the process should be started, such as command name +/// and arguments, as well as runtime information on the status of the actual physical process which +/// represents it. Shellscript functions, builtins and blocks of code may all need to spawn an +/// external process that handles the piping and redirecting of IO for them. +/// +/// If the process is of type EXTERNAL or INTERNAL_EXEC, argv is the argument array and actual_cmd +/// is the absolute path of the command to execute. +/// +/// If the process is of type INTERNAL_BUILTIN, argv is the argument vector, and argv[0] is the name +/// of the builtin command. +/// +/// If the process is of type INTERNAL_FUNCTION, argv is the argument vector, and argv[0] is the +/// name of the shellscript function. +class process_t { + private: null_terminated_array_t argv_array; io_chain_t process_io_chain; - /* No copying */ + // No copying. 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_EXEC - */ + /// Type of process. Can be one of \c EXTERNAL, \c INTERNAL_BUILTIN, \c INTERNAL_FUNCTION, \c + /// INTERNAL_EXEC. enum process_type_t type; - /* For internal block processes only, the node offset of the block */ + /// For internal block processes only, the node offset of the block. node_offset_t internal_block_node; - /** Sets argv */ - void set_argv(const wcstring_list_t &argv) - { - argv_array.set(argv); - } + /// Sets argv. + void set_argv(const wcstring_list_t &argv) { argv_array.set(argv); } - /** Returns argv */ - const wchar_t * const *get_argv(void) const - { - return argv_array.get(); - } - const null_terminated_array_t &get_argv_array(void) const - { - return argv_array; - } + /// Returns argv. + const wchar_t *const *get_argv(void) const { return argv_array.get(); } + const null_terminated_array_t &get_argv_array(void) const { return argv_array; } - /** Returns argv[idx] */ - const wchar_t *argv(size_t idx) const - { - const wchar_t * const *argv = argv_array.get(); + /// Returns argv[idx]. + const wchar_t *argv(size_t idx) const { + const wchar_t *const *argv = argv_array.get(); assert(argv != NULL); return argv[idx]; } - /** Returns argv[0], or NULL */ - const wchar_t *argv0() const - { - const wchar_t * const *argv = argv_array.get(); + /// Returns argv[0], or NULL. + const wchar_t *argv0() const { + const wchar_t *const *argv = argv_array.get(); return argv ? argv[0] : NULL; } - /* IO chain getter and setter */ - const io_chain_t &io_chain() const - { - return process_io_chain; - } + /// IO chain getter and setter. + const io_chain_t &io_chain() const { return process_io_chain; } - void set_io_chain(const io_chain_t &chain) - { - this->process_io_chain = chain; - } + void set_io_chain(const io_chain_t &chain) { this->process_io_chain = chain; } - /** actual command to pass to exec in case of EXTERNAL or INTERNAL_EXEC. */ + /// Actual command to pass to exec in case of EXTERNAL or INTERNAL_EXEC. wcstring actual_cmd; - - /** process ID */ + /// Process ID pid_t pid; - - /** File descriptor that pipe output should bind to */ + /// File descriptor that pipe output should bind to. int pipe_write_fd; - - /** File descriptor that the _next_ process pipe input should bind to */ + /// File descriptor that the _next_ process pipe input should bind to. int pipe_read_fd; - - /** true if process has completed */ + /// True if process has completed. volatile int completed; - - /** true if process has stopped */ + /// True if process has stopped. volatile int stopped; - - /** reported status value */ + /// Reported status value. volatile int status; - - /** Special flag to tell the evaluation function for count to print the help information */ + /// 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. */ + /// 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 */ + /// Last time of cpu time check. struct timeval last_time; - /** Number of jiffies spent in process at last cpu time check */ + /// 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 -{ - /** Whether the user has been told about stopped job */ +/// Constants for the flag variable in the job struct. +enum { + /// Whether the user has been told about stopped job. JOB_NOTIFIED = 1 << 0, - - /** Whether this job is in the foreground */ + /// Whether this job is in the foreground. JOB_FOREGROUND = 1 << 1, - - /** - Whether the specified job is completely constructed, - i.e. completely parsed, and every process in the job has been - forked, etc. - */ + /// Whether the specified job is completely constructed, i.e. completely parsed, and every + /// process in the job has been forked, etc. JOB_CONSTRUCTED = 1 << 2, - - /** Whether the specified job is a part of a subshell, event handler or some other form of special job that should not be reported */ + /// Whether the specified job is a part of a subshell, event handler or some other form of + /// special job that should not be reported. JOB_SKIP_NOTIFICATION = 1 << 3, - - /** Whether the exit status should be negated. This flag can only be set by the not builtin. */ + /// Whether the exit status should be negated. This flag can only be set by the not builtin. JOB_NEGATE = 1 << 4, - - /** Whether the job is under job control */ + /// Whether the job is under job control. JOB_CONTROL = 1 << 5, - - /** Whether the job wants to own the terminal when in the foreground */ + /// Whether the job wants to own the terminal when in the foreground. JOB_TERMINAL = 1 << 6 }; @@ -256,147 +172,93 @@ typedef int job_id_t; job_id_t acquire_job_id(void); void release_job_id(job_id_t jobid); -/** - A struct represeting a job. A job is basically a pipeline of one - or more processes and a couple of flags. - */ -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. - */ +/// A struct represeting a job. A job is basically a pipeline of one or more processes and a couple +/// of flags. +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 IO chain associated with the block */ + // The IO chain associated with the block. const io_chain_t block_io; - /* No copying */ + // No copying. job_t(const job_t &rhs); void operator=(const job_t &); -public: - + public: job_t(job_id_t jobid, const io_chain_t &bio); ~job_t(); - /** Returns whether the command is empty. */ - bool command_is_empty() const - { - return command_str.empty(); - } + /// Returns whether the command is empty. + bool command_is_empty() const { return command_str.empty(); } - /** Returns the command as a wchar_t *. */ - const wchar_t *command_wcstr() const - { - return command_str.c_str(); - } + /// Returns the command as a wchar_t *. */ + const wchar_t *command_wcstr() const { return command_str.c_str(); } - /** Returns the command */ - const wcstring &command() const - { - return command_str; - } + /// Returns the command. + const wcstring &command() const { return command_str; } - /** Sets the command */ - void set_command(const wcstring &cmd) - { - command_str = cmd; - } + /// Sets the command. + void set_command(const wcstring &cmd) { command_str = cmd; } - /** - A linked list of all the processes in this job. We are responsible for deleting this when we are deallocated. - */ + /// 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. - */ + /// 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. - */ + /// 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. - */ + /// 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; - - /** - Bitset containing information about the job. A combination of the JOB_* constants. - */ + /// Bitset containing information about the job. A combination of the JOB_* constants. unsigned int flags; - /* Returns the block IO redirections associated with the job. These are things like the IO redirections associated with the begin...end statement. */ - const io_chain_t &block_io_chain() const - { - return this->block_io; - } + /// Returns the block IO redirections associated with the job. These are things like the IO + /// redirections associated with the begin...end statement. + const io_chain_t &block_io_chain() const { return this->block_io; } - /* Fetch all the IO redirections associated with the job */ + /// Fetch all the IO redirections associated with the job. io_chain_t all_io_redirections() const; }; -/** - Whether we are running a subshell command -*/ +/// Whether we are running a subshell command. extern int is_subshell; -/** - Whether we are running a block of commands -*/ +/// Whether we are running a block of commands. extern int is_block; -/** - Whether we are reading from the keyboard right now -*/ +/// Whether we are reading from the keyboard right now. int get_is_interactive(void); -/** - Whether this shell is attached to the keyboard at all -*/ +/// Whether this shell is attached to the keyboard at all. extern int is_interactive_session; -/** - Whether we are a login shell -*/ +/// Whether we are a login shell. extern int is_login; -/** - Whether we are running an event handler -*/ +/// Whether we are running an event handler. extern int is_event; - typedef std::list job_list_t; 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 -{ - job_list_t * const job_list; +/// 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 { + 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; } @@ -408,177 +270,111 @@ class job_iterator_t size_t count() const; }; -/** - Whether a universal variable barrier roundtrip has already been - made for the currently executing command. Such a roundtrip only - needs to be done once on a given command, unless a universal - variable value is changed. Once this has been done, this variable - is set to 1, so that no more roundtrips need to be done. - - Both setting it to one when it should be zero and the opposite may - cause concurrency bugs. -*/ +/// Whether a universal variable barrier roundtrip has already been made for the currently executing +/// command. Such a roundtrip only needs to be done once on a given command, unless a universal +/// variable value is changed. Once this has been done, this variable is set to 1, so that no more +/// roundtrips need to be done. +/// +/// Both setting it to one when it should be zero and the opposite may cause concurrency bugs. bool get_proc_had_barrier(); void set_proc_had_barrier(bool flag); -/** - Pid of last process started in the background -*/ +/// Pid of last process started in the background. extern pid_t proc_last_bg_pid; -/** - The current job control mode. - - Must be one of JOB_CONTROL_ALL, JOB_CONTROL_INTERACTIVE and JOB_CONTROL_NONE -*/ +/// The current job control mode. +/// +/// Must be one of JOB_CONTROL_ALL, JOB_CONTROL_INTERACTIVE and JOB_CONTROL_NONE. extern int job_control_mode; -/** - If this flag is set, fish will never fork or run execve. It is used - to put fish into a syntax verifier mode where fish tries to validate - the syntax of a file but doesn't actually do anything. - */ +/// If this flag is set, fish will never fork or run execve. It is used to put fish into a syntax +/// verifier mode where fish tries to validate the syntax of a file but doesn't actually do +/// anything. extern int no_exec; -/** - Add the specified flag to the bitset of flags for the specified job - */ +/// Add the specified flag to the bitset of flags for the specified job. void job_set_flag(job_t *j, unsigned int flag, int set); -/** - Returns one if the specified flag is set in the specified job, 0 otherwise. - */ +/// Returns one if the specified flag is set in the specified job, 0 otherwise. int job_get_flag(const job_t *j, unsigned int flag); -/** - Sets the status of the last process to exit -*/ +/// Sets the status of the last process to exit. void proc_set_last_status(int s); -/** - Returns the status of the last process to exit -*/ +/// Returns the status of the last process to exit. int proc_get_last_status(); -/** - Remove the specified job -*/ -void job_free(job_t* j); +/// Remove the specified job. +void job_free(job_t *j); -/** - Promotes a job to the front of the job list. -*/ +/// Promotes a job to the front of the job list. void job_promote(job_t *job); -/** - Return the job with the specified job id. - If id is 0 or less, return the last job used. -*/ +/// Return the job with the specified job id. If id is 0 or less, return the last job used. job_t *job_get(job_id_t id); -/** - Return the job with the specified pid. -*/ +/// Return the job with the specified pid. job_t *job_get_from_pid(int pid); -/** - Tests if the job is stopped -*/ +/// Tests if the job is stopped. int job_is_stopped(const job_t *j); -/** - Tests if the job has completed, i.e. if the last process of the pipeline has ended. -*/ +/// Tests if the job has completed, i.e. if the last process of the pipeline has ended. bool job_is_completed(const job_t *j); -/** - Reassume a (possibly) stopped job. Put job j in the foreground. If - cont is true, restore the saved terminal modes and send the - process group a SIGCONT signal to wake it up before we block. - - \param j The job - \param cont Whether the function should wait for the job to complete before returning -*/ +/// Reassume a (possibly) stopped job. Put job j in the foreground. If cont is true, restore the +/// saved terminal modes and send the process group a SIGCONT signal to wake it up before we block. +/// +/// \param j The job +/// \param cont Whether the function should wait for the job to complete before returning void job_continue(job_t *j, bool cont); -/** - Notify the user about stopped or terminated jobs. Delete terminated - jobs from the job list. - - \param interactive whether interactive jobs should be reaped as well -*/ +/// Notify the user about stopped or terminated jobs. Delete terminated jobs from the job list. +/// +/// \param interactive whether interactive jobs should be reaped as well int job_reap(bool interactive); -/** - Signal handler for SIGCHLD. Mark any processes with relevant - information. -*/ +/// Signal handler for SIGCHLD. Mark any processes with relevant information. void job_handle_signal(int signal, siginfo_t *info, void *con); -/** - Send the specified signal to all processes in the specified job. -*/ +/// Send the specified signal to all processes in the specified job. int job_signal(job_t *j, int signal); -/** - Mark a process as failed to execute (and therefore completed) -*/ +/// Mark a process as failed to execute (and therefore completed). void job_mark_process_as_failed(const job_t *job, process_t *p); #ifdef HAVE__PROC_SELF_STAT -/** - Use the procfs filesystem to look up how many jiffies of cpu time - was used by this process. This function is only available on - systems with the procfs file entry 'stat', i.e. Linux. -*/ +/// Use the procfs filesystem to look up how many jiffies of cpu time 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); -/** - Update process time usage for all processes by calling the - proc_get_jiffies function for every process of every job. -*/ +/// Update process time usage for all processes by calling the proc_get_jiffies function for every +/// process of every job. void proc_update_jiffies(); - #endif -/** - Perform a set of simple sanity checks on the job list. This - includes making sure that only one job is in the foreground, that - every process is in a valid state, etc. -*/ +/// Perform a set of simple sanity checks on the job list. This includes making sure that only one +/// job is in the foreground, that every process is in a valid state, etc. void proc_sanity_check(); -/** - Send a process/job exit event notification. This function is a - convenience wrapper around event_fire(). -*/ +/// Send a process/job exit event notification. This function is a convenience wrapper around +/// event_fire(). void proc_fire_event(const wchar_t *msg, int type, pid_t pid, int status); -/** - Initializations -*/ +/// Initializations. void proc_init(); -/** - Clean up before exiting -*/ +/// Clean up before exiting. void proc_destroy(); -/** - Set new value for is_interactive flag, saving previous value. If - needed, update signal handlers. -*/ +/// Set new value for is_interactive flag, saving previous value. If needed, update signal handlers. void proc_push_interactive(int value); -/** - Set is_interactive flag to the previous value. If needed, update - signal handlers. -*/ +/// Set is_interactive flag to the previous value. If needed, update signal handlers. void proc_pop_interactive(); -/** - Format an exit status code as returned by e.g. wait into a fish exit code number as accepted by proc_set_last_status. - */ +/// Format an exit status code as returned by e.g. wait into a fish exit code number as accepted by +/// proc_set_last_status. int proc_format_status(int status); #endif From c76d86631717929b3a2f259615e8603e69e13256 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Mon, 2 May 2016 22:20:53 -0700 Subject: [PATCH 206/363] Suppress another pointless IWYU warning --- src/fish_tests.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index b3a309786..d0be58055 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -1,5 +1,7 @@ // Various bug and feature tests. Compiled and run by make test. + // IWYU pragma: no_include +// IWYU pragma: no_include #include #include #include From ed086fb1c88956da8046a0834aaedadeb3a7064c Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 3 May 2016 16:29:15 +0200 Subject: [PATCH 207/363] Bind btab also in vi-bindings It wasn't inherited. Fixes #2964. --- share/functions/fish_default_key_bindings.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/functions/fish_default_key_bindings.fish b/share/functions/fish_default_key_bindings.fish index 3e9fb1960..e54913219 100644 --- a/share/functions/fish_default_key_bindings.fish +++ b/share/functions/fish_default_key_bindings.fish @@ -133,7 +133,7 @@ function fish_default_key_bindings -d "Default (Emacs-like) key bindings for fis bind $argv \ep '__fish_paginate' # shift-tab does a tab complete followed by a search - bind --key btab complete-and-search + bind $argv --key btab complete-and-search # escape cancels stuff bind \e cancel From 835176ef324b049d5195a0276b8d7eb1b3b85c47 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Tue, 3 May 2016 10:36:22 -0700 Subject: [PATCH 208/363] restyle reader module to match project style Reduces lint errors from 338 to 205 (-39%). Line count from 4650 to 3654 (-21%). Another step in resolving issue #2902. --- src/reader.cpp | 3731 ++++++++++++++++++------------------------------ src/reader.h | 325 ++--- 2 files changed, 1530 insertions(+), 2526 deletions(-) diff --git a/src/reader.cpp b/src/reader.cpp index fb7cc5f28..7b6234888 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -1,171 +1,139 @@ -/** \file reader.c - -Functions for reading data from stdin and passing to the -parser. If stdin is a keyboard, it supplies a killring, history, -syntax highlighting, tab-completion and various other interactive features. - -Internally the interactive mode functions rely in the functions of the -input library to read individual characters of input. - -Token search is handled incrementally. Actual searches are only done -on when searching backwards, since the previous results are saved. The -last search position is remembered and a new search continues from the -last search position. All search results are saved in the list -'search_prev'. When the user searches forward, i.e. presses Alt-down, -the list is consulted for previous search result, and subsequent -backwards searches are also handled by consulting the list up until -the end of the list is reached, at which point regular searching will -commence. - -*/ +// Functions for reading data from stdin and passing to the parser. If stdin is a keyboard, it +// supplies a killring, history, syntax highlighting, tab-completion and various other interactive +// features. +// +// Internally the interactive mode functions rely in the functions of the input library to read +// individual characters of input. +// +// Token search is handled incrementally. Actual searches are only done on when searching backwards, +// since the previous results are saved. The last search position is remembered and a new search +// continues from the last search position. All search results are saved in the list 'search_prev'. +// When the user searches forward, i.e. presses Alt-down, the list is consulted for previous search +// result, and subsequent backwards searches are also handled by consulting the list up until the +// end of the list is reached, at which point regular searching will commence. #include "config.h" // IWYU pragma: no_include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include #include #include -#include #include #include +#include #include -#include #ifdef HAVE_SIGINFO_H #include #endif #ifdef HAVE_SYS_SELECT_H #include #endif -#include -#include -#include #include +#include +#include #include +#include #include -#include "fallback.h" // IWYU pragma: keep -#include "util.h" -#include "wutil.h" // IWYU pragma: keep -#include "highlight.h" -#include "reader.h" -#include "proc.h" -#include "parser.h" -#include "complete.h" -#include "history.h" +#include "color.h" #include "common.h" -#include "sanity.h" +#include "complete.h" #include "env.h" +#include "event.h" #include "exec.h" #include "expand.h" -#include "tokenizer.h" -#include "kill.h" -#include "input_common.h" -#include "input.h" +#include "fallback.h" // IWYU pragma: keep #include "function.h" -#include "output.h" -#include "signal.h" -#include "screen.h" -#include "iothread.h" +#include "highlight.h" +#include "history.h" +#include "input.h" +#include "input_common.h" #include "intern.h" -#include "parse_util.h" -#include "parse_tree.h" -#include "pager.h" -#include "color.h" -#include "event.h" #include "io.h" +#include "iothread.h" +#include "kill.h" +#include "output.h" +#include "pager.h" #include "parse_constants.h" +#include "parse_tree.h" +#include "parse_util.h" +#include "parser.h" +#include "proc.h" +#include "reader.h" +#include "sanity.h" +#include "screen.h" +#include "signal.h" +#include "tokenizer.h" +#include "util.h" +#include "wutil.h" // IWYU pragma: keep -/** - Maximum length of prefix string when printing completion - list. Longer prefixes will be ellipsized. -*/ +/// Maximum length of prefix string when printing completion list. Longer prefixes will be +/// ellipsized. #define PREFIX_MAX_LEN 9 -/** - A simple prompt for reading shell commands that does not rely on - fish specific commands, meaning it will work even if fish is not - installed. This is used by read_i. -*/ +/// A simple prompt for reading shell commands that does not rely on fish specific commands, meaning +/// it will work even if fish is not installed. This is used by read_i. #define DEFAULT_PROMPT L"echo -n \"$USER@\"(hostname|cut -d . -f 1)' '(__fish_pwd)'> '" -/** - The name of the function that prints the fish prompt - */ +/// The name of the function that prints the fish prompt. #define LEFT_PROMPT_FUNCTION_NAME L"fish_prompt" -/** - The name of the function that prints the fish right prompt (RPROMPT) - */ +/// The name of the function that prints the fish right prompt (RPROMPT). #define RIGHT_PROMPT_FUNCTION_NAME L"fish_right_prompt" - -/* The name of the function for getting the input mode indicator */ +/// The name of the function for getting the input mode indicator. #define MODE_PROMPT_FUNCTION_NAME L"fish_mode_prompt" - -/** - The default title for the reader. This is used by reader_readline. -*/ +/// The default title for the reader. This is used by reader_readline. #define DEFAULT_TITLE L"echo $_ \" \"; __fish_pwd" -/** - The maximum number of characters to read from the keyboard without - repainting. Note that this readahead will only occur if new - characters are available for reading, fish will never block for - more input without repainting. -*/ +/// The maximum number of characters to read from the keyboard without repainting. Note that this +/// readahead will only occur if new characters are available for reading, fish will never block for +/// more input without repainting. #define READAHEAD_MAX 256 -/** - A mode for calling the reader_kill function. In this mode, the new - string is appended to the current contents of the kill buffer. - */ +/// A mode for calling the reader_kill function. In this mode, the new string is appended to the +/// current contents of the kill buffer. #define KILL_APPEND 0 -/** - A mode for calling the reader_kill function. In this mode, the new - string is prepended to the current contents of the kill buffer. - */ + +/// A mode for calling the reader_kill function. In this mode, the new string is prepended to the +/// current contents of the kill buffer. #define KILL_PREPEND 1 -/** - History search mode. This value means that no search is currently - performed. - */ +/// History search mode. This value means that no search is currently performed. #define NO_SEARCH 0 -/** - History search mode. This value means that we are performing a line - history search. - */ + +/// History search mode. This value means that we are performing a line history search. #define LINE_SEARCH 1 -/** - History search mode. This value means that we are performing a token - history search. - */ + +/// History search mode. This value means that we are performing a token history search. #define TOKEN_SEARCH 2 -/** - History search mode. This value means we are searching backwards. - */ +/// History search mode. This value means we are searching backwards. #define SEARCH_BACKWARD 0 -/** - History search mode. This value means we are searching forwards. - */ + +/// History search mode. This value means we are searching forwards. #define SEARCH_FORWARD 1 -/* Any time the contents of a buffer changes, we update the generation count. This allows for our background highlighting thread to notice it and skip doing work that it would otherwise have to do. This variable should really be of some kind of interlocked or atomic type that guarantees we're not reading stale cache values. With C++11 we should use atomics, but until then volatile should work as well, at least on x86.*/ +/// Any time the contents of a buffer changes, we update the generation count. This allows for our +/// background highlighting thread to notice it and skip doing work that it would otherwise have to +/// do. This variable should really be of some kind of interlocked or atomic type that guarantees +/// we're not reading stale cache values. With C++11 we should use atomics, but until then volatile +/// should work as well, at least on x86. static volatile unsigned int s_generation_count; -/* This pthreads generation count is set when an autosuggestion background thread starts up, so it can easily check if the work it is doing is no longer useful. */ +/// This pthreads generation count is set when an autosuggestion background thread starts up, so it +/// can easily check if the work it is doing is no longer useful. static pthread_key_t generation_count_key; static void set_command_line_and_position(editable_line_t *el, const wcstring &new_str, size_t pos); -void editable_line_t::insert_string(const wcstring &str, size_t start, size_t len) -{ - // Clamp the range to something valid +void editable_line_t::insert_string(const wcstring &str, size_t start, size_t len) { + // Clamp the range to something valid. size_t string_length = str.size(); start = mini(start, string_length); len = mini(len, string_length - start); @@ -173,418 +141,279 @@ void editable_line_t::insert_string(const wcstring &str, size_t start, size_t le this->position += len; } -/** - A struct describing the state of the interactive reader. These - states can be stacked, in case reader_readline() calls are - nested. This happens when the 'read' builtin is used. -*/ -class reader_data_t -{ -public: - - /** String containing the whole current commandline */ +/// A struct describing the state of the interactive reader. These states can be stacked, in case +/// reader_readline() calls are nested. This happens when the 'read' builtin is used. +class reader_data_t { + public: + /// String containing the whole current commandline. editable_line_t command_line; - - /** String containing the autosuggestion */ + /// String containing the autosuggestion. wcstring autosuggestion; - - /** Current pager */ + /// Current pager. pager_t pager; - - /** Current page rendering */ + /// Current page rendering. page_rendering_t current_page_rendering; - - /** Whether autosuggesting is allowed at all */ + /// Whether autosuggesting is allowed at all. bool allow_autosuggestion; - - /** When backspacing, we temporarily suppress autosuggestions */ + /// When backspacing, we temporarily suppress autosuggestions. bool suppress_autosuggestion; - - /** Whether abbreviations are expanded */ + /// Whether abbreviations are expanded. bool expand_abbreviations; - - /** The representation of the current screen contents */ + /// The representation of the current screen contents. screen_t screen; - - /** The history */ + /// The history. history_t *history; - - /** - String containing the current search item - */ + /// String containing the current search item. wcstring search_buff; - - /* History search */ + /// History search. history_search_t history_search; - - /** - Saved position used by token history search - */ + /// Saved position used by token history search. size_t token_history_pos; - - /** - Saved search string for token history search. Not handled by command_line_changed. - */ + /// 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. - */ + /// List for storing previous search results. Used to avoid duplicates. wcstring_list_t search_prev; - - /** The current position in search_prev */ + /// The current position in search_prev. size_t search_pos; + /// Indicates whether a selection is currently active. + bool sel_active; + /// The position of the cursor, when selection was initiated. + size_t sel_begin_pos; + /// The start position of the current selection, if one. + size_t sel_start_pos; + /// The stop position of the current selection, if one. + size_t sel_stop_pos; + /// Name of the current application. + wcstring app_name; + /// 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; + /// Completion support. + wcstring cycle_command_line; + size_t cycle_cursor_pos; + /// 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; + /// Function for tab completion. + complete_function_t complete_func; + /// Function for syntax highlighting. + highlight_function_t highlight_function; + /// Function for testing if the string can be returned. + parser_test_error_bits_t (*test_func)(const wchar_t *); + /// 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; + /// The current contents of the top item in the kill ring. + wcstring kill_item; + /// 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; + /// Keep track of whether any internal code has done something which is known to require a + /// repaint. + bool repaint_needed; + /// Whether a screen reset is needed after a repaint. + bool screen_reset_needed; + /// Whether the reader should exit on ^C. + bool exit_on_interrupt; - bool is_navigating_pager_contents() const - { - return this->pager.is_navigating_contents(); - } + bool is_navigating_pager_contents() const { return this->pager.is_navigating_contents(); } - /* The line that is currently being edited. Typically the command line, but may be the search field */ - editable_line_t *active_edit_line() - { - if (this->is_navigating_pager_contents() && this->pager.is_search_field_shown()) - { + /// The line that is currently being edited. Typically the command line, but may be the search + /// field. + editable_line_t *active_edit_line() { + if (this->is_navigating_pager_contents() && this->pager.is_search_field_shown()) { return &this->pager.search_field_line; - } - else - { + } else { return &this->command_line; } } - /** Do what we need to do whenever our command line changes */ + /// Do what we need to do whenever our command line changes. void command_line_changed(const editable_line_t *el); - /** Do what we need to do whenever our pager selection */ + /// Do what we need to do whenever our pager selection. void pager_selection_changed(); - /** Expand abbreviations at the current cursor position, minus backtrack_amt. */ + /// Expand abbreviations at the current cursor position, minus backtrack_amt. bool expand_abbreviation_as_necessary(size_t cursor_backtrack); - /** Indicates whether a selection is currently active */ - bool sel_active; - - /** The position of the cursor, when selection was initiated. */ - size_t sel_begin_pos; - - /** The start position of the current selection, if one. */ - size_t sel_start_pos; - - /** The stop position of the current selection, if one. */ - size_t sel_stop_pos; - - /** Name of the current application */ - wcstring app_name; - - /** 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; - - /* Completion support */ - wcstring cycle_command_line; - size_t cycle_cursor_pos; - - /** - 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; - - /** - Function for tab completion - */ - complete_function_t complete_func; - - /** - Function for syntax highlighting - */ - highlight_function_t highlight_function; - - /** - Function for testing if the string can be returned - */ - parser_test_error_bits_t (*test_func)(const wchar_t *); - - /** - 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; - - /** The current contents of the top item in the kill ring. */ - wcstring kill_item; - - /** - 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; - - /** - Keep track of whether any internal code has done something - which is known to require a repaint. - */ - bool repaint_needed; - - /** Whether a screen reset is needed after a repaint. */ - bool screen_reset_needed; - - /** Whether the reader should exit on ^C. */ - bool exit_on_interrupt; - - /** Constructor */ - reader_data_t() : - allow_autosuggestion(0), - suppress_autosuggestion(0), - expand_abbreviations(0), - history(0), - token_history_pos(0), - search_pos(0), - sel_active(0), - sel_begin_pos(0), - sel_start_pos(0), - sel_stop_pos(0), - cycle_cursor_pos(0), - complete_func(0), - highlight_function(0), - test_func(0), - end_loop(0), - prev_end_loop(0), - next(0), - search_mode(0), - repaint_needed(0), - screen_reset_needed(0), - exit_on_interrupt(0) - { - } + /// Constructor + reader_data_t() + : allow_autosuggestion(0), + suppress_autosuggestion(0), + expand_abbreviations(0), + history(0), + token_history_pos(0), + search_pos(0), + sel_active(0), + sel_begin_pos(0), + sel_start_pos(0), + sel_stop_pos(0), + cycle_cursor_pos(0), + complete_func(0), + highlight_function(0), + test_func(0), + end_loop(0), + prev_end_loop(0), + next(0), + search_mode(0), + repaint_needed(0), + screen_reset_needed(0), + exit_on_interrupt(0) {} }; -/* Sets the command line contents, without clearing the pager */ +/// Sets the command line contents, without clearing the pager. static void reader_set_buffer_maintaining_pager(const wcstring &b, size_t pos); -/* Clears the pager */ +/// Clears the pager. static void clear_pager(); -/** - The current interactive reading context -*/ -static reader_data_t *data=0; +/// The current interactive reading context. +static reader_data_t *data = 0; -/** - This flag is set to true when fish is interactively reading from - stdin. It changes how a ^C is handled by the fish interrupt - handler. -*/ +/// This flag is set to true when fish is interactively reading from stdin. It changes how a ^C is +/// handled by the fish interrupt handler. static int is_interactive_read; -/** - Flag for ending non-interactive shell -*/ +/// Flag for ending non-interactive shell. static int end_loop = 0; -/** The stack containing names of files that are being parsed */ +/// The stack containing names of files that are being parsed. static std::stack > current_filename; -/** - This variable is set to true by the signal handler when ^C is pressed -*/ -static volatile int interrupted=0; - - -/* - Prototypes for a bunch of functions defined later on. -*/ +/// This variable is set to true by the signal handler when ^C is pressed. +static volatile int interrupted = 0; +// Prototypes for a bunch of functions defined later on. static bool is_backslashed(const wcstring &str, size_t pos); static wchar_t unescaped_quote(const wcstring &str, size_t pos); -/** Mode on startup, which we restore on exit */ +/// Mode on startup, which we restore on exit. static struct termios terminal_mode_on_startup; -/** Mode we use to execute programs */ +/// Mode we use to execute programs. static struct termios terminal_mode_for_executing_programs; - static void reader_super_highlight_me_plenty(int highlight_pos_adjust = 0, bool no_io = false); -/** - Variable to keep track of forced exits - see \c reader_exit_forced(); -*/ +/// Variable to keep track of forced exits - see \c reader_exit_forced(); static int exit_forced; - -/** - Give up control of terminal -*/ -static void term_donate() -{ +/// Give up control of terminal. +static void term_donate() { set_color(rgb_color_t::normal(), rgb_color_t::normal()); - while (1) - { - if (tcsetattr(0, TCSANOW, &terminal_mode_for_executing_programs)) - { - if (errno != EINTR) - { + while (1) { + if (tcsetattr(0, TCSANOW, &terminal_mode_for_executing_programs)) { + if (errno != EINTR) { debug(1, _(L"Could not set terminal mode for new job")); wperror(L"tcsetattr"); break; } - } - else + } else break; } - - } - -/** - Update the cursor position -*/ -static void update_buff_pos(editable_line_t *el, size_t buff_pos) -{ +/// Update the cursor position. +static void update_buff_pos(editable_line_t *el, size_t buff_pos) { el->position = buff_pos; - if (el == &data->command_line && data->sel_active) - { - if (data->sel_begin_pos <= buff_pos) - { + if (el == &data->command_line && data->sel_active) { + if (data->sel_begin_pos <= buff_pos) { data->sel_start_pos = data->sel_begin_pos; data->sel_stop_pos = buff_pos; - } - else - { + } else { data->sel_start_pos = buff_pos; data->sel_stop_pos = data->sel_begin_pos; } } } - -/** - Grab control of terminal -*/ -static void term_steal() -{ - - while (1) - { - if (tcsetattr(0,TCSANOW,&shell_modes)) - { - if (errno != EINTR) - { +/// Grab control of terminal. +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 + } else break; } common_handle_winch(0); - } -int reader_exit_forced() -{ - return exit_forced; -} +int reader_exit_forced() { return exit_forced; } -/* Given a command line and an autosuggestion, return the string that gets shown to the user */ -wcstring combine_command_and_autosuggestion(const wcstring &cmdline, const wcstring &autosuggestion) -{ - // We want to compute the full line, containing the command line and the autosuggestion - // They may disagree on whether characters are uppercase or lowercase - // Here we do something funny: if the last token of the command line contains any uppercase characters, we use its case - // Otherwise we use the case of the autosuggestion - // This is an idea from https://github.com/fish-shell/fish-shell/issues/335 +/// Given a command line and an autosuggestion, return the string that gets shown to the user. +wcstring combine_command_and_autosuggestion(const wcstring &cmdline, + const wcstring &autosuggestion) { + // We want to compute the full line, containing the command line and the autosuggestion They may + // disagree on whether characters are uppercase or lowercase Here we do something funny: if the + // last token of the command line contains any uppercase characters, we use its case. Otherwise + // we use the case of the autosuggestion. This is an idea from issue #335. wcstring full_line; - if (autosuggestion.size() <= cmdline.size() || cmdline.empty()) - { - // No or useless autosuggestion, or no command line + if (autosuggestion.size() <= cmdline.size() || cmdline.empty()) { + // No or useless autosuggestion, or no command line. full_line = cmdline; - } - else if (string_prefixes_string(cmdline, autosuggestion)) - { - // No case disagreements, or no extra characters in the autosuggestion + } else if (string_prefixes_string(cmdline, autosuggestion)) { + // No case disagreements, or no extra characters in the autosuggestion. full_line = autosuggestion; - } - else - { - // We have an autosuggestion which is not a prefix of the command line, i.e. a case disagreement - // Decide whose case we want to use + } else { + // We have an autosuggestion which is not a prefix of the command line, i.e. a case + // disagreement. Decide whose case we want to use. const wchar_t *begin = NULL, *cmd = cmdline.c_str(); parse_util_token_extent(cmd, cmdline.size() - 1, &begin, NULL, NULL, NULL); bool last_token_contains_uppercase = false; - if (begin) - { + if (begin) { const wchar_t *end = begin + wcslen(begin); last_token_contains_uppercase = (std::find_if(begin, end, iswupper) != end); } - if (! last_token_contains_uppercase) - { - // Use the autosuggestion's case + if (!last_token_contains_uppercase) { + // Use the autosuggestion's case. full_line = autosuggestion; - } - else - { - // Use the command line case for its characters, then append the remaining characters in the autosuggestion - // Note that we know that autosuggestion.size() > cmdline.size() due to the first test above + } else { + // Use the command line case for its characters, then append the remaining characters in + // the autosuggestion. Note that we know that autosuggestion.size() > cmdline.size() due + // to the first test above. full_line = cmdline; - full_line.append(autosuggestion, cmdline.size(), autosuggestion.size() - cmdline.size()); + full_line.append(autosuggestion, cmdline.size(), + autosuggestion.size() - cmdline.size()); } } return full_line; } -/** - Repaint the entire commandline. This means reset and clear the - commandline, write the prompt, perform syntax highlighting, write - the commandline and move the cursor. -*/ -static void reader_repaint() -{ +/// Repaint the entire commandline. This means reset and clear the commandline, write the prompt, +/// perform syntax highlighting, write the commandline and move the cursor. +static void reader_repaint() { editable_line_t *cmd_line = &data->command_line; - // Update the indentation + // Update the indentation. data->indents = parse_util_compute_indents(cmd_line->text); - // Combine the command and autosuggestion into one string + // Combine the command and autosuggestion into one string. wcstring full_line = combine_command_and_autosuggestion(cmd_line->text, data->autosuggestion); size_t len = full_line.size(); - if (len < 1) - len = 1; + if (len < 1) len = 1; std::vector colors = data->colors; colors.resize(len, highlight_spec_autosuggestion); - if (data->sel_active) - { + if (data->sel_active) { highlight_spec_t selection_color = highlight_make_background(highlight_spec_selection); - for (size_t i = data->sel_start_pos; i <= std::min(len - 1, data->sel_stop_pos); i++) - { + for (size_t i = data->sel_start_pos; i <= std::min(len - 1, data->sel_stop_pos); i++) { colors[i] = selection_color; } } @@ -592,59 +421,42 @@ static void reader_repaint() std::vector indents = data->indents; indents.resize(len); - // Re-render our completions page if necessary - // We set the term size to 1 less than the true term height. This means we will always show the (bottom) line of the prompt. + // Re-render our completions page if necessary. We set the term size to 1 less than the true + // term height. This means we will always show the (bottom) line of the prompt. data->pager.set_term_size(maxi(1, common_get_width()), maxi(1, common_get_height() - 1)); data->pager.update_rendering(&data->current_page_rendering); bool focused_on_pager = data->active_edit_line() == &data->pager.search_field_line; size_t cursor_position = focused_on_pager ? data->pager.cursor_position() : cmd_line->position; - s_write(&data->screen, - data->left_prompt_buff, - data->right_prompt_buff, - full_line, - cmd_line->size(), - &colors[0], - &indents[0], - cursor_position, - data->sel_start_pos, - data->sel_stop_pos, - data->current_page_rendering, - focused_on_pager); + s_write(&data->screen, data->left_prompt_buff, data->right_prompt_buff, full_line, + cmd_line->size(), &colors[0], &indents[0], cursor_position, data->sel_start_pos, + data->sel_stop_pos, data->current_page_rendering, focused_on_pager); data->repaint_needed = false; } -/** Internal helper function for handling killing parts of text. */ -static void reader_kill(editable_line_t *el, size_t begin_idx, size_t length, int mode, int newv) -{ +/// Internal helper function for handling killing parts of text. +static void reader_kill(editable_line_t *el, size_t begin_idx, size_t length, int mode, int newv) { const wchar_t *begin = el->text.c_str() + begin_idx; - if (newv) - { + if (newv) { data->kill_item = wcstring(begin, length); kill_add(data->kill_item); - } - else - { + } 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 (el->position > begin_idx) - { - /* Move the buff position back by the number of characters we deleted, but don't go past buff_pos */ + if (el->position > 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(el->position - begin_idx, length); update_buff_pos(el, el->position - backtrack); } @@ -656,154 +468,138 @@ static void reader_kill(editable_line_t *el, size_t begin_idx, size_t length, in reader_repaint(); } - -/* This is called from a signal handler! */ -void reader_handle_int(int sig) -{ - if (!is_interactive_read) - { +// This is called from a signal handler! +void reader_handle_int(int sig) { + if (!is_interactive_read) { parser_t::skip_all_blocks(); } interrupted = 1; - } -const wchar_t *reader_current_filename() -{ +const wchar_t *reader_current_filename() { ASSERT_IS_MAIN_THREAD(); return current_filename.empty() ? NULL : current_filename.top(); } - -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)); } - -void reader_pop_current_filename() -{ +void reader_pop_current_filename() { ASSERT_IS_MAIN_THREAD(); current_filename.pop(); } - -/** Make sure buffers are large enough to hold the current string length */ -void reader_data_t::command_line_changed(const editable_line_t *el) -{ +/// Make sure buffers are large enough to hold the current string length. +void reader_data_t::command_line_changed(const editable_line_t *el) { ASSERT_IS_MAIN_THREAD(); - if (el == &this->command_line) - { + if (el == &this->command_line) { size_t len = this->command_line.size(); - /* 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. */ + // 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. highlight_spec_t last_color = colors.empty() ? highlight_spec_t() : colors.back(); colors.resize(len, last_color); indents.resize(len); - /* Update the gen count */ + // Update the gen count. s_generation_count++; - } - else if (el == &this->pager.search_field_line) - { + } else if (el == &this->pager.search_field_line) { this->pager.refilter_completions(); this->pager_selection_changed(); } } -void reader_data_t::pager_selection_changed() -{ +void reader_data_t::pager_selection_changed() { ASSERT_IS_MAIN_THREAD(); const completion_t *completion = this->pager.selected_completion(this->current_page_rendering); - /* Update the cursor and command line */ + // Update the cursor and command line. size_t cursor_pos = this->cycle_cursor_pos; wcstring new_cmd_line; - if (completion == NULL) - { + if (completion == NULL) { new_cmd_line = this->cycle_command_line; - } - else - { - new_cmd_line = completion_apply_to_command_line(completion->completion, completion->flags, this->cycle_command_line, &cursor_pos, false); + } else { + new_cmd_line = + completion_apply_to_command_line(completion->completion, completion->flags, + this->cycle_command_line, &cursor_pos, false); } reader_set_buffer_maintaining_pager(new_cmd_line, cursor_pos); - /* Since we just inserted a completion, don't immediately do a new autosuggestion */ + // Since we just inserted a completion, don't immediately do a new autosuggestion. this->suppress_autosuggestion = true; - /* Trigger repaint (see #765) */ + // Trigger repaint (see issue #765). reader_repaint_needed(); } -/* Expand abbreviations at the given cursor position. Does NOT inspect 'data'. */ -bool reader_expand_abbreviation_in_command(const wcstring &cmdline, size_t cursor_pos, wcstring *output) -{ - /* See if we are at "command position". Get the surrounding command substitution, and get the extent of the first token. */ - const wchar_t * const buff = cmdline.c_str(); +/// Expand abbreviations at the given cursor position. Does NOT inspect 'data'. +bool reader_expand_abbreviation_in_command(const wcstring &cmdline, size_t cursor_pos, + wcstring *output) { + // See if we are at "command position". Get the surrounding command substitution, and get the + // extent of the first token. + const wchar_t *const buff = cmdline.c_str(); const wchar_t *cmdsub_begin = NULL, *cmdsub_end = NULL; parse_util_cmdsubst_extent(buff, cursor_pos, &cmdsub_begin, &cmdsub_end); assert(cmdsub_begin != NULL && cmdsub_begin >= buff); assert(cmdsub_end != NULL && cmdsub_end >= cmdsub_begin); - /* Determine the offset of this command substitution */ + // Determine the offset of this command substitution. const size_t subcmd_offset = cmdsub_begin - buff; const wcstring subcmd = wcstring(cmdsub_begin, cmdsub_end - cmdsub_begin); const size_t subcmd_cursor_pos = cursor_pos - subcmd_offset; - /* Parse this subcmd */ + // Parse this subcmd. parse_node_tree_t parse_tree; - parse_tree_from_string(subcmd, parse_flag_continue_after_error | parse_flag_accept_incomplete_tokens, &parse_tree, NULL); + parse_tree_from_string(subcmd, + parse_flag_continue_after_error | parse_flag_accept_incomplete_tokens, + &parse_tree, NULL); - /* Look for plain statements where the cursor is at the end of the command */ + // Look for plain statements where the cursor is at the end of the command. const parse_node_t *matching_cmd_node = NULL; const size_t len = parse_tree.size(); - for (size_t i=0; i < len; i++) - { + for (size_t i = 0; i < len; i++) { const parse_node_t &node = parse_tree.at(i); - /* Only interested in plain statements with source */ - if (node.type != symbol_plain_statement || ! node.has_source()) - continue; + // Only interested in plain statements with source. + if (node.type != symbol_plain_statement || !node.has_source()) continue; - /* Skip decorated statements */ + // Skip decorated statements. if (parse_tree.decoration_for_plain_statement(node) != parse_statement_decoration_none) continue; - /* Get the command node. Skip it if we can't or it has no source */ + // Get the command node. Skip it if we can't or it has no source. const parse_node_t *cmd_node = parse_tree.get_child(node, 0, parse_token_type_string); - if (cmd_node == NULL || ! cmd_node->has_source()) - continue; + if (cmd_node == NULL || !cmd_node->has_source()) continue; - /* Now see if its source range contains our cursor, including at the end */ - if (subcmd_cursor_pos >= cmd_node->source_start && subcmd_cursor_pos <= cmd_node->source_start + cmd_node->source_length) - { - /* Success! */ + // Now see if its source range contains our cursor, including at the end. + if (subcmd_cursor_pos >= cmd_node->source_start && + subcmd_cursor_pos <= cmd_node->source_start + cmd_node->source_length) { + // Success! matching_cmd_node = cmd_node; break; } } - /* Now if we found a command node, expand it */ + // Now if we found a command node, expand it. bool result = false; - if (matching_cmd_node != NULL) - { + if (matching_cmd_node != NULL) { assert(matching_cmd_node->type == parse_token_type_string); const wcstring token = matching_cmd_node->get_source(subcmd); wcstring abbreviation; - if (expand_abbreviation(token, &abbreviation)) - { - /* There was an abbreviation! Replace the token in the full command. Maintain the relative position of the cursor. */ - if (output != NULL) - { + if (expand_abbreviation(token, &abbreviation)) { + // There was an abbreviation! Replace the token in the full command. Maintain the + // relative position of the cursor. + if (output != NULL) { output->assign(cmdline); - output->replace(subcmd_offset + matching_cmd_node->source_start, matching_cmd_node->source_length, abbreviation); + output->replace(subcmd_offset + matching_cmd_node->source_start, + matching_cmd_node->source_length, abbreviation); } result = true; } @@ -811,22 +607,21 @@ bool reader_expand_abbreviation_in_command(const wcstring &cmdline, size_t curso return result; } -/* Expand abbreviations at the current cursor position, minus the given cursor backtrack. This may change the command line but does NOT repaint it. This is to allow the caller to coalesce repaints. */ -bool reader_data_t::expand_abbreviation_as_necessary(size_t cursor_backtrack) -{ +/// Expand abbreviations at the current cursor position, minus the given cursor backtrack. This may +/// change the command line but does NOT repaint it. This is to allow the caller to coalesce +/// repaints. +bool reader_data_t::expand_abbreviation_as_necessary(size_t cursor_backtrack) { bool result = false; editable_line_t *el = data->active_edit_line(); - if (this->expand_abbreviations && el == &data->command_line) - { - /* Try expanding abbreviations */ + if (this->expand_abbreviations && el == &data->command_line) { + // Try expanding abbreviations. wcstring new_cmdline; size_t cursor_pos = el->position - mini(el->position, cursor_backtrack); - if (reader_expand_abbreviation_in_command(el->text, cursor_pos, &new_cmdline)) - { - /* We expanded an abbreviation! The cursor moves by the difference in the command line lengths. */ + if (reader_expand_abbreviation_in_command(el->text, cursor_pos, &new_cmdline)) { + // We expanded an abbreviation! The cursor moves by the difference in the command line + // lengths. size_t new_buff_pos = el->position + new_cmdline.size() - el->text.size(); - el->text.swap(new_cmdline); update_buff_pos(el, new_buff_pos); data->command_line_changed(el); @@ -836,90 +631,68 @@ bool reader_data_t::expand_abbreviation_as_necessary(size_t cursor_backtrack) return result; } -void reader_reset_interrupted() -{ - interrupted = 0; -} +void reader_reset_interrupted() { interrupted = 0; } -int reader_interrupted() -{ +int reader_interrupted() { int res = interrupted; - if (res) - { - interrupted=0; + if (res) { + interrupted = 0; } return res; } -int reader_reading_interrupted() -{ +int reader_reading_interrupted() { int res = reader_interrupted(); - if (res && data && data->exit_on_interrupt) - { + if (res && data && data->exit_on_interrupt) { reader_exit(1, 0); parser_t::skip_all_blocks(); - // We handled the interrupt ourselves, our caller doesn't need to - // handle it. + // We handled the interrupt ourselves, our caller doesn't need to handle it. return 0; } return res; } -bool reader_thread_job_is_stale() -{ +bool reader_thread_job_is_stale() { ASSERT_IS_BACKGROUND_THREAD(); - return (void*)(uintptr_t) s_generation_count != pthread_getspecific(generation_count_key); + return (void *)(uintptr_t)s_generation_count != pthread_getspecific(generation_count_key); } -void reader_write_title(const wcstring &cmd, bool reset_cursor_position) -{ +void reader_write_title(const wcstring &cmd, bool reset_cursor_position) { 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 believe 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; + // 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 believe 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; const wchar_t *term = term_str.c_str(); bool recognized = false; recognized = recognized || contains(term, L"xterm", L"screen", L"nxterm", L"rxvt"); - recognized = recognized || ! wcsncmp(term, L"xterm-", wcslen(L"xterm-")); - recognized = recognized || ! wcsncmp(term, L"screen-", wcslen(L"screen-")); + recognized = recognized || !wcsncmp(term, L"xterm-", wcslen(L"xterm-")); + recognized = recognized || !wcsncmp(term, L"screen-", wcslen(L"screen-")); - if (! recognized) - { + if (!recognized) { char *n = ttyname(STDIN_FILENO); - if (contains(term, L"linux")) - { + if (contains(term, L"linux")) { return; } - if (contains(term, L"dumb")) - return; + if (contains(term, L"dumb")) return; - if (strstr(n, "tty") || strstr(n, "/vc/")) - return; + if (strstr(n, "tty") || strstr(n, "/vc/")) return; } wcstring fish_title_command = DEFAULT_TITLE; - if (function_exists(L"fish_title")) - { + if (function_exists(L"fish_title")) { fish_title_command = L"fish_title"; - if (! cmd.empty()) - { + if (!cmd.empty()) { fish_title_command.append(L" "); fish_title_command.append(parse_util_escape_string_with_quote(cmd, L'\0')); } @@ -928,13 +701,10 @@ void reader_write_title(const wcstring &cmd, bool reset_cursor_position) wcstring_list_t lst; proc_push_interactive(0); - if (exec_subshell(fish_title_command, lst, false /* do not apply exit status */) != -1) - { - if (! lst.empty()) - { + if (exec_subshell(fish_title_command, lst, false /* do not apply exit status */) != -1) { + if (!lst.empty()) { writestr(L"\x1b]0;"); - for (size_t i=0; iprompt_buff. -*/ -static void exec_prompt() -{ - /* Clear existing prompts */ +/// Reexecute the prompt command. The output is inserted into data->prompt_buff. +static void exec_prompt() { + // Clear existing prompts. data->left_prompt_buff.clear(); data->right_prompt_buff.clear(); - /* Do not allow the exit status of the prompts to leak through */ + // Do not allow the exit status of the prompts to leak through. const bool apply_exit_status = false; - /* If we have any prompts, they must be run non-interactively */ - if (data->left_prompt.size() || data->right_prompt.size()) - { + // If we have any prompts, they must be run non-interactively. + if (data->left_prompt.size() || data->right_prompt.size()) { proc_push_interactive(0); - // Prepend any mode indicator to the left prompt (#1988) - if (function_exists(MODE_PROMPT_FUNCTION_NAME)) - { + // Prepend any mode indicator to the left prompt (issue #1988). + if (function_exists(MODE_PROMPT_FUNCTION_NAME)) { wcstring_list_t mode_indicator_list; exec_subshell(MODE_PROMPT_FUNCTION_NAME, mode_indicator_list, apply_exit_status); - // We do not support multiple lines in the mode indicator, so just concatenate all of them - for (size_t i = 0; i < mode_indicator_list.size(); i++) - { + // We do not support multiple lines in the mode indicator, so just concatenate all of + // them. + for (size_t i = 0; i < mode_indicator_list.size(); i++) { data->left_prompt_buff += mode_indicator_list.at(i); } } - if (! data->left_prompt.empty()) - { + if (!data->left_prompt.empty()) { wcstring_list_t prompt_list; - // ignore return status + // Ignore return status. exec_subshell(data->left_prompt, prompt_list, apply_exit_status); - 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.empty()) - { + if (!data->right_prompt.empty()) { wcstring_list_t prompt_list; - // status is ignored + // Status is ignored. exec_subshell(data->right_prompt, prompt_list, apply_exit_status); - for (size_t i = 0; i < prompt_list.size(); i++) - { - // Right prompt does not support multiple lines, so just concatenate all of them + 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); } } @@ -1004,149 +765,119 @@ static void exec_prompt() proc_pop_interactive(); } - // Write the screen title. - // Do not reset the cursor position: exec_prompt is called when there may still be output - // on the line from the previous command (#2499) and we need our PROMPT_SP hack to work + // Write the screen title. Do not reset the cursor position: exec_prompt is called when there + // may still be output on the line from the previous command (#2499) and we need our PROMPT_SP + // hack to work. reader_write_title(L"", false); } -void reader_init() -{ +void reader_init() { VOMIT_ON_FAILURE(pthread_key_create(&generation_count_key, NULL)); - /* Save the initial terminal mode */ + // Save the initial terminal mode. tcgetattr(STDIN_FILENO, &terminal_mode_on_startup); - /* Set the mode used for program execution, initialized to the current mode */ - memcpy(&terminal_mode_for_executing_programs, &terminal_mode_on_startup, sizeof terminal_mode_for_executing_programs); - terminal_mode_for_executing_programs.c_iflag &= ~IXON; /* disable flow control */ - terminal_mode_for_executing_programs.c_iflag &= ~IXOFF; /* disable flow control */ + // Set the mode used for program execution, initialized to the current mode. + memcpy(&terminal_mode_for_executing_programs, &terminal_mode_on_startup, + sizeof terminal_mode_for_executing_programs); + terminal_mode_for_executing_programs.c_iflag &= ~IXON; // disable flow control + terminal_mode_for_executing_programs.c_iflag &= ~IXOFF; // disable flow control - /* Set the mode used for the terminal, initialized to the current mode */ + // Set the mode used for the terminal, initialized to the current mode. memcpy(&shell_modes, &terminal_mode_on_startup, sizeof shell_modes); - shell_modes.c_iflag &= ~ICRNL; /* turn off mapping CR (\cM) to NL (\cJ) */ - shell_modes.c_iflag &= ~INLCR; /* turn off mapping NL (\cJ) to CR (\cM) */ - shell_modes.c_lflag &= ~ICANON; /* turn off canonical mode */ - shell_modes.c_lflag &= ~ECHO; /* turn off echo mode */ - shell_modes.c_iflag &= ~IXON; /* disable flow control */ - shell_modes.c_iflag &= ~IXOFF; /* disable flow control */ - shell_modes.c_cc[VMIN]=1; - shell_modes.c_cc[VTIME]=0; + shell_modes.c_iflag &= ~ICRNL; // turn off mapping CR (\cM) to NL (\cJ) + shell_modes.c_iflag &= ~INLCR; // turn off mapping NL (\cJ) to CR (\cM) + shell_modes.c_lflag &= ~ICANON; // turn off canonical mode + shell_modes.c_lflag &= ~ECHO; // turn off echo mode + shell_modes.c_iflag &= ~IXON; // disable flow control + shell_modes.c_iflag &= ~IXOFF; // disable flow control + shell_modes.c_cc[VMIN] = 1; + shell_modes.c_cc[VTIME] = 0; #if defined(_POSIX_VDISABLE) - // 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 +// 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 #endif - // We don't use term_steal because this can fail if fd 0 isn't associated - // with a tty and this function is run regardless of whether stdin is tied - // to a tty. This is harmless in that case. We do it unconditionally - // because disabling ICRNL mode (see above) needs to be done at the - // earliest possible moment. Doing it here means it will be done within - // approximately 1 ms of the start of the shell rather than 250 ms (or - // more) when reader_interactive_init is eventually called. + // We don't use term_steal because this can fail if fd 0 isn't associated with a tty and this + // function is run regardless of whether stdin is tied to a tty. This is harmless in that case. + // We do it unconditionally because disabling ICRNL mode (see above) needs to be done at the + // earliest possible moment. Doing it here means it will be done within approximately 1 ms of + // the start of the shell rather than 250 ms (or more) when reader_interactive_init is + // eventually called. // // TODO: Remove this condition when issue #2315 and #1041 are addressed. - if (is_interactive_session) - { - tcsetattr(STDIN_FILENO, TCSANOW,&shell_modes); + if (is_interactive_session) { + tcsetattr(STDIN_FILENO, TCSANOW, &shell_modes); } } +void reader_destroy() { pthread_key_delete(generation_count_key); } -void reader_destroy() -{ - pthread_key_delete(generation_count_key); -} - -void restore_term_mode() -{ - // Restore the term mode if we own the terminal - // It's important we do this before restore_foreground_process_group, otherwise we won't think we own the terminal - if (getpid() == tcgetpgrp(STDIN_FILENO)) - { +void restore_term_mode() { + // Restore the term mode if we own the terminal. It's important we do this before + // restore_foreground_process_group, otherwise we won't think we own the terminal. + if (getpid() == tcgetpgrp(STDIN_FILENO)) { tcsetattr(STDIN_FILENO, TCSANOW, &terminal_mode_on_startup); } } -void reader_exit(int do_exit, int forced) -{ - if (data) - data->end_loop=do_exit; - end_loop=do_exit; - if (forced) - exit_forced = 1; - +void reader_exit(int do_exit, int forced) { + if (data) data->end_loop = do_exit; + end_loop = do_exit; + if (forced) exit_forced = 1; } -void reader_repaint_needed() -{ - if (data) - { +void reader_repaint_needed() { + if (data) { data->repaint_needed = true; } } -void reader_repaint_if_needed() -{ - if (data == NULL) - return; +void reader_repaint_if_needed() { + if (data == NULL) return; bool needs_reset = data->screen_reset_needed; bool needs_repaint = needs_reset || data->repaint_needed; - if (needs_reset) - { + if (needs_reset) { exec_prompt(); s_reset(&data->screen, screen_reset_current_line_and_prompt); data->screen_reset_needed = false; } - if (needs_repaint) - { - reader_repaint(); - /* reader_repaint clears repaint_needed */ + if (needs_repaint) { + reader_repaint(); // reader_repaint clears repaint_needed } } -static void reader_repaint_if_needed_one_arg(void * unused) -{ - reader_repaint_if_needed(); -} +static void reader_repaint_if_needed_one_arg(void *unused) { reader_repaint_if_needed(); } -void reader_react_to_color_change() -{ - if (! data) - return; +void reader_react_to_color_change() { + if (!data) return; - if (! data->repaint_needed || ! data->screen_reset_needed) - { + if (!data->repaint_needed || !data->screen_reset_needed) { data->repaint_needed = true; data->screen_reset_needed = true; input_common_add_callback(reader_repaint_if_needed_one_arg, NULL); } } - -/* Indicates if the given command char ends paging */ -static bool command_ends_paging(wchar_t c, bool focused_on_search_field) -{ - switch (c) - { - /* These commands always end paging */ +/// Indicates if the given command char ends paging. +static bool command_ends_paging(wchar_t c, bool focused_on_search_field) { + switch (c) { case R_HISTORY_SEARCH_BACKWARD: case R_HISTORY_SEARCH_FORWARD: case R_HISTORY_TOKEN_SEARCH_BACKWARD: case R_HISTORY_TOKEN_SEARCH_FORWARD: case R_ACCEPT_AUTOSUGGESTION: - case R_CANCEL: + case R_CANCEL: { + // These commands always end paging. return true; - - /* These commands never do */ + } case R_COMPLETE: case R_COMPLETE_AND_SEARCH: case R_BACKWARD_CHAR: @@ -1158,14 +889,15 @@ static bool command_ends_paging(wchar_t c, bool focused_on_search_field) case R_SUPPRESS_AUTOSUGGESTION: case R_BEGINNING_OF_HISTORY: case R_END_OF_HISTORY: - default: + default: { + // These commands never do. return false; - - /* R_EXECUTE does end paging, but only executes if it was not paging. So it's handled specially */ - case R_EXECUTE: + } + case R_EXECUTE: { + // R_EXECUTE does end paging, but only executes if it was not paging. So it's handled + // specially. return false; - - /* These commands operate on the search field if that's where the focus is */ + } case R_BEGINNING_OF_LINE: case R_END_OF_LINE: case R_FORWARD_WORD: @@ -1193,31 +925,28 @@ static bool command_ends_paging(wchar_t c, bool focused_on_search_field) case R_VI_ARG_DIGIT: case R_VI_DELETE_TO: case R_BEGINNING_OF_BUFFER: - case R_END_OF_BUFFER: - return ! focused_on_search_field; + case R_END_OF_BUFFER: { + // These commands operate on the search field if that's where the focus is. + return !focused_on_search_field; + } } } -/** - Remove the previous character in the character buffer and on the - screen using syntax highlighting, etc. -*/ -static void remove_backward() -{ +/// Remove the previous character in the character buffer and on the screen using syntax +/// highlighting, etc. +static void remove_backward() { editable_line_t *el = data->active_edit_line(); - if (el->position <= 0) - return; + if (el->position <= 0) return; - /* Fake composed character sequences by continuing to delete until we delete a character of width at least 1. */ + // Fake composed character sequences by continuing to delete until we delete a character of + // width at least 1. int width; - do - { + do { update_buff_pos(el, el->position - 1); width = fish_wcwidth(el->text.at(el->position)); el->text.erase(el->position, 1); - } - while (width == 0 && el->position > 0); + } while (width == 0 && el->position > 0); data->command_line_changed(el); data->suppress_autosuggestion = true; @@ -1226,39 +955,39 @@ static void remove_backward() reader_repaint_needed(); } - -/** - Insert the characters of the string into the command line buffer - and print them to the screen using syntax highlighting, etc. - Optionally also expand abbreviations, after space characters. - Returns true if the string changed. -*/ -static bool insert_string(editable_line_t *el, const wcstring &str, bool allow_expand_abbreviations = false) -{ +/// Insert the characters of the string into the command line buffer and print them to the screen +/// using syntax highlighting, etc. Optionally also expand abbreviations, after space characters. +/// Returns true if the string changed. +static bool insert_string(editable_line_t *el, const wcstring &str, + bool allow_expand_abbreviations = false) { size_t len = str.size(); - if (len == 0) - return false; + if (len == 0) return false; - /* Start inserting. If we are expanding abbreviations, we have to do this after every space (see #1434), so look for spaces. We try to do this efficiently (rather than the simpler character at a time) to avoid expensive work in command_line_changed() */ + // Start inserting. If we are expanding abbreviations, we have to do this after every space (see + // #1434), so look for spaces. We try to do this efficiently (rather than the simpler character + // at a time) to avoid expensive work in command_line_changed(). size_t cursor = 0; - while (cursor < len) - { - /* Determine the position of the next expansion-triggering char (possibly none), and the end of the range we wish to insert */ + while (cursor < len) { + // Determine the position of the next expansion-triggering char (possibly none), and the end + // of the range we wish to insert. const wchar_t *expansion_triggering_chars = L" ;|&^><"; - size_t char_triggering_expansion_pos = allow_expand_abbreviations ? str.find_first_of(expansion_triggering_chars, cursor) : wcstring::npos; + size_t char_triggering_expansion_pos = + allow_expand_abbreviations ? str.find_first_of(expansion_triggering_chars, cursor) + : wcstring::npos; bool has_expansion_triggering_char = (char_triggering_expansion_pos != wcstring::npos); - size_t range_end = (has_expansion_triggering_char ? char_triggering_expansion_pos + 1 : len); + size_t range_end = + (has_expansion_triggering_char ? char_triggering_expansion_pos + 1 : len); - /* Insert from the cursor up to but not including the range end */ + // Insert from the cursor up to but not including the range end. assert(range_end > cursor); el->insert_string(str, cursor, range_end - cursor); update_buff_pos(el, el->position); data->command_line_changed(el); - /* If we got an expansion trigger, then the last character we inserted was it (i.e. was a space). Expand abbreviations. */ - if (has_expansion_triggering_char && allow_expand_abbreviations) - { + // If we got an expansion trigger, then the last character we inserted was it (i.e. was a + // space). Expand abbreviations. + if (has_expansion_triggering_char && allow_expand_abbreviations) { assert(range_end > 0); assert(wcschr(expansion_triggering_chars, str.at(range_end - 1))); data->expand_abbreviation_as_necessary(1); @@ -1266,11 +995,11 @@ static bool insert_string(editable_line_t *el, const wcstring &str, bool allow_e cursor = range_end; } - if (el == &data->command_line) - { + if (el == &data->command_line) { 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(el->position > 0); reader_super_highlight_me_plenty(-1); } @@ -1280,29 +1009,28 @@ static bool insert_string(editable_line_t *el, const wcstring &str, bool allow_e return true; } -/** - Insert the character into the command line buffer and print it to - the screen using syntax highlighting, etc. -*/ -static bool insert_char(editable_line_t *el, wchar_t c, bool allow_expand_abbreviations = false) -{ +/// Insert the character into the command line buffer and print it to the screen using syntax +/// highlighting, etc. +static bool insert_char(editable_line_t *el, wchar_t c, bool allow_expand_abbreviations = false) { return insert_string(el, wcstring(1, c), allow_expand_abbreviations); } - -/** - Insert the string in the given command line at the given cursor - position. The function checks if the string is quoted or not and - correctly escapes the string. - \param val the string to insert - \param flags A union of all flags describing the completion to insert. See the completion_t struct for more information on possible values. - \param command_line The command line into which we will insert - \param inout_cursor_pos On input, the location of the cursor within the command line. On output, the new desired position. - \param append_only Whether we can only append to the command line, or also modify previous characters. This is used to determine whether we go inside a trailing quote. - \return The completed string -*/ -wcstring completion_apply_to_command_line(const wcstring &val_str, complete_flags_t flags, const wcstring &command_line, size_t *inout_cursor_pos, bool append_only) -{ +/// Insert the string in the given command line at the given cursor position. The function checks if +/// the string is quoted or not and correctly escapes the string. +/// +/// \param val the string to insert +/// \param flags A union of all flags describing the completion to insert. See the completion_t +/// struct for more information on possible values. +/// \param command_line The command line into which we will insert +/// \param inout_cursor_pos On input, the location of the cursor within the command line. On output, +/// the new desired position. +/// \param append_only Whether we can only append to the command line, or also modify previous +/// characters. This is used to determine whether we go inside a trailing quote. +/// +/// \return The completed string +wcstring completion_apply_to_command_line(const wcstring &val_str, complete_flags_t flags, + const wcstring &command_line, size_t *inout_cursor_pos, + bool append_only) { const wchar_t *val = val_str.c_str(); bool add_space = !(flags & COMPLETE_NO_SPACE); bool do_replace = !!(flags & COMPLETE_REPLACES_TOKEN); @@ -1311,8 +1039,7 @@ wcstring completion_apply_to_command_line(const wcstring &val_str, complete_flag const size_t cursor_pos = *inout_cursor_pos; bool back_into_trailing_quote = false; - if (do_replace) - { + if (do_replace) { size_t move_cursor; const wchar_t *begin, *end; @@ -1322,23 +1049,19 @@ wcstring completion_apply_to_command_line(const wcstring &val_str, complete_flag wcstring sb(buff, begin - buff); - if (do_escape) - { - /* Respect COMPLETE_DONT_ESCAPE_TILDES */ + if (do_escape) { + // Respect COMPLETE_DONT_ESCAPE_TILDES. bool no_tilde = !!(flags & COMPLETE_DONT_ESCAPE_TILDES); - wcstring escaped = escape(val, ESCAPE_ALL | ESCAPE_NO_QUOTED | (no_tilde ? ESCAPE_NO_TILDE : 0)); + wcstring escaped = + escape(val, ESCAPE_ALL | ESCAPE_NO_QUOTED | (no_tilde ? ESCAPE_NO_TILDE : 0)); sb.append(escaped); move_cursor = escaped.size(); - } - else - { + } else { sb.append(val); move_cursor = wcslen(val); } - - if (add_space) - { + if (add_space) { sb.append(L" "); move_cursor += 1; } @@ -1347,52 +1070,48 @@ wcstring completion_apply_to_command_line(const wcstring &val_str, complete_flag 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) - { - /* Note that we ignore COMPLETE_DONT_ESCAPE_TILDES here. We get away with this because unexpand_tildes only operates on completions that have COMPLETE_REPLACES_TOKEN set, but we ought to respect them */ + if (do_escape) { + // Note that we ignore COMPLETE_DONT_ESCAPE_TILDES here. We get away with this because + // unexpand_tildes only operates on completions that have COMPLETE_REPLACES_TOKEN set, + // but we ought to respect them. parse_util_get_parameter_info(command_line, cursor_pos, "e, NULL, NULL); - /* If the token is reported as unquoted, but ends with a (unescaped) quote, and we can modify the command line, then delete the trailing quote so that we can insert within the quotes instead of after them. See https://github.com/fish-shell/fish-shell/issues/552 */ - if (quote == L'\0' && ! append_only && cursor_pos > 0) - { - /* The entire token is reported as unquoted...see if the last character is an unescaped quote */ + // If the token is reported as unquoted, but ends with a (unescaped) quote, and we can + // modify the command line, then delete the trailing quote so that we can insert within + // the quotes instead of after them. See issue #552. + if (quote == L'\0' && !append_only && cursor_pos > 0) { + // The entire token is reported as unquoted...see if the last character is an + // unescaped quote. wchar_t trailing_quote = unescaped_quote(command_line, cursor_pos - 1); - if (trailing_quote != L'\0') - { + if (trailing_quote != L'\0') { quote = trailing_quote; back_into_trailing_quote = true; } } replaced = parse_util_escape_string_with_quote(val_str, quote); - } - else - { + } else { replaced = val; } size_t insertion_point = cursor_pos; - if (back_into_trailing_quote) - { - /* Move the character back one so we enter the terminal quote */ + if (back_into_trailing_quote) { + // Move the character back one so we enter the terminal quote. assert(insertion_point > 0); insertion_point--; } - /* Perform the insertion and compute the new location */ + // Perform the insertion and compute the new location. wcstring result = command_line; result.insert(insertion_point, replaced); - size_t new_cursor_pos = insertion_point + replaced.size() + (back_into_trailing_quote ? 1 : 0); - if (add_space) - { - if (quote != L'\0' && unescaped_quote(command_line, insertion_point) != quote) - { - /* This is a quoted parameter, first print a quote */ + size_t new_cursor_pos = + insertion_point + replaced.size() + (back_into_trailing_quote ? 1 : 0); + if (add_space) { + if (quote != L'\0' && unescaped_quote(command_line, insertion_point) != quote) { + // This is a quoted parameter, first print a quote. result.insert(new_cursor_pos++, wcstring("e, 1)); } result.insert(new_cursor_pos++, L" "); @@ -1402,28 +1121,24 @@ wcstring completion_apply_to_command_line(const wcstring &val_str, complete_flag } } -/** - Insert the string at the current cursor position. The function - checks if the string is quoted or not and correctly escapes the - string. - - \param val the string to insert - \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, complete_flags_t flags) -{ +/// Insert the string at the current cursor position. The function checks if the string is quoted or +/// not and correctly escapes the string. +/// +/// \param val the string to insert +/// \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, complete_flags_t flags) { editable_line_t *el = data->active_edit_line(); size_t cursor = el->position; - wcstring new_command_line = completion_apply_to_command_line(val, flags, el->text, &cursor, false /* not append only */); + wcstring new_command_line = completion_apply_to_command_line(val, flags, el->text, &cursor, + false /* not append only */); reader_set_buffer_maintaining_pager(new_command_line, cursor); - /* Since we just inserted a completion, don't immediately do a new autosuggestion */ + // Since we just inserted a completion, don't immediately do a new autosuggestion. data->suppress_autosuggestion = true; } -struct autosuggestion_context_t -{ +struct autosuggestion_context_t { wcstring search_string; wcstring autosuggestion; size_t cursor_pos; @@ -1433,78 +1148,67 @@ struct autosuggestion_context_t const env_vars_snapshot_t vars; const unsigned int generation_count; - autosuggestion_context_t(history_t *history, const wcstring &term, size_t pos) : - search_string(term), - cursor_pos(pos), - searcher(*history, term, HISTORY_SEARCH_TYPE_PREFIX), - detector(history), - working_directory(env_get_pwd_slash()), - vars(env_vars_snapshot_t::highlighting_keys), - generation_count(s_generation_count) - { - } + autosuggestion_context_t(history_t *history, const wcstring &term, size_t pos) + : search_string(term), + cursor_pos(pos), + searcher(*history, term, HISTORY_SEARCH_TYPE_PREFIX), + detector(history), + working_directory(env_get_pwd_slash()), + vars(env_vars_snapshot_t::highlighting_keys), + generation_count(s_generation_count) {} - /* The function run in the background thread to determine an autosuggestion */ - int threaded_autosuggest(void) - { + // The function run in the background thread to determine an autosuggestion. + int threaded_autosuggest(void) { ASSERT_IS_BACKGROUND_THREAD(); - /* If the main thread has moved on, skip all the work */ - if (generation_count != s_generation_count) - { + // If the main thread has moved on, skip all the work. + if (generation_count != s_generation_count) { return 0; } - VOMIT_ON_FAILURE(pthread_setspecific(generation_count_key, (void*)(uintptr_t) generation_count)); + VOMIT_ON_FAILURE( + pthread_setspecific(generation_count_key, (void *)(uintptr_t)generation_count)); - /* Let's make sure we aren't using the empty string */ - if (search_string.empty()) - { + // Let's make sure we aren't using the empty string. + if (search_string.empty()) { return 0; } - while (! reader_thread_job_is_stale() && searcher.go_backwards()) - { + while (!reader_thread_job_is_stale() && 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; + // Skip items with newlines because they make terrible autosuggestions. + if (item.str().find('\n') != wcstring::npos) continue; - if (autosuggest_validate_from_history(item, detector, working_directory, vars)) - { - /* The command autosuggestion was handled specially, so we're done */ + 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; } } - /* Maybe cancel here */ - if (reader_thread_job_is_stale()) - return 0; + // Maybe cancel here. + if (reader_thread_job_is_stale()) return 0; - // 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 - // while you go back to edit a line + // 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 while you go back to edit a line const wchar_t last_char = search_string.at(search_string.size() - 1); const bool cursor_at_end = (this->cursor_pos == search_string.size()); - if (! cursor_at_end && iswspace(last_char)) - return 0; + if (!cursor_at_end && iswspace(last_char)) return 0; - /* On the other hand, if the line ends with a quote, don't go dumping stuff after the quote */ - if (wcschr(L"'\"", last_char) && cursor_at_end) - return 0; + // On the other hand, if the line ends with a quote, don't go dumping stuff after the quote. + if (wcschr(L"'\"", last_char) && cursor_at_end) return 0; - /* Try normal completions */ + // Try normal completions. std::vector completions; complete(search_string, &completions, COMPLETION_REQUEST_AUTOSUGGESTION, vars); completions_sort_and_prioritize(&completions); - 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, comp.flags, this->search_string, &cursor, true /* append only */); + this->autosuggestion = completion_apply_to_command_line( + comp.completion, comp.flags, this->search_string, &cursor, true /* append only */); return 1; } @@ -1512,30 +1216,23 @@ struct autosuggestion_context_t } }; -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) -{ - /* 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. */ +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 editable_line_t *el = data->active_edit_line(); const wchar_t *whitespace = L" \t\r\n\v"; - return ! data->suppress_autosuggestion && - data->history_search.is_at_end() && - el == &data->command_line && - el->text.find_first_not_of(whitespace) != wcstring::npos; + return !data->suppress_autosuggestion && data->history_search.is_at_end() && + el == &data->command_line && el->text.find_first_not_of(whitespace) != wcstring::npos; } -static void autosuggest_completed(autosuggestion_context_t *ctx, int result) -{ - if (result && - can_autosuggest() && - ctx->search_string == data->command_line.text && - 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 */ +static void autosuggest_completed(autosuggestion_context_t *ctx, int result) { + if (result && can_autosuggest() && ctx->search_string == data->command_line.text && + 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(); reader_repaint(); @@ -1543,42 +1240,36 @@ static void autosuggest_completed(autosuggestion_context_t *ctx, int result) delete ctx; } - -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. */ +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. 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()) { const editable_line_t *el = data->active_edit_line(); - autosuggestion_context_t *ctx = new autosuggestion_context_t(data->history, el->text, el->position); + autosuggestion_context_t *ctx = + new autosuggestion_context_t(data->history, el->text, el->position); iothread_perform(threaded_autosuggest, autosuggest_completed, ctx); } } -/* Accept any autosuggestion by replacing the command line with it. If full is true, take the whole thing; if it's false, then take only the first "word" */ -static void accept_autosuggestion(bool full) -{ - if (! data->autosuggestion.empty()) - { - /* Accepting an autosuggestion clears the pager */ +// Accept any autosuggestion by replacing the command line with it. If full is true, take the whole +// thing; if it's false, then take only the first "word". +static void accept_autosuggestion(bool full) { + if (!data->autosuggestion.empty()) { + // Accepting an autosuggestion clears the pager. clear_pager(); - /* Accept the autosuggestion */ - if (full) - { - /* Just take the whole thing */ + // Accept the autosuggestion. + if (full) { + // Just take the whole thing. data->command_line.text = data->autosuggestion; - } - else - { - /* Accept characters up to a word separator */ + } else { + // Accept characters up to a word separator. move_word_state_machine_t state(move_word_style_punctuation); - for (size_t idx = data->command_line.size(); idx < data->autosuggestion.size(); idx++) - { + for (size_t idx = data->command_line.size(); idx < data->autosuggestion.size(); idx++) { wchar_t wc = data->autosuggestion.at(idx); - if (! state.consume_char(wc)) - break; + if (!state.consume_char(wc)) break; data->command_line.text.push_back(wc); } } @@ -1589,40 +1280,32 @@ static void accept_autosuggestion(bool full) } } -/* Ensure we have no pager contents */ -static void clear_pager() -{ - if (data) - { +// Ensure we have no pager contents. +static void clear_pager() { + if (data) { data->pager.clear(); data->current_page_rendering = page_rendering_t(); reader_repaint_needed(); } } -static void select_completion_in_direction(enum selection_direction_t dir) -{ +static void select_completion_in_direction(enum selection_direction_t dir) { assert(data != NULL); - bool selection_changed = data->pager.select_next_completion_in_direction(dir, data->current_page_rendering); - if (selection_changed) - { + bool selection_changed = + data->pager.select_next_completion_in_direction(dir, data->current_page_rendering); + if (selection_changed) { data->pager_selection_changed(); } } -/** - Flash the screen. This function changes the color of the - current line momentarily and sends a BEL to maybe flash the - screen or emite a sound, depending on how it is configured. -*/ -static void reader_flash() -{ +/// Flash the screen. This function changes the color of the current line momentarily and sends a +/// BEL to maybe flash the screen or emite a sound, depending on how it is configured. +static void reader_flash() { struct timespec pollint; editable_line_t *el = &data->command_line; - for (size_t i=0; iposition; i++) - { - data->colors.at(i) = highlight_spec_search_match<<16; + for (size_t i = 0; i < el->position; i++) { + data->colors.at(i) = highlight_spec_search_match << 16; } reader_repaint(); @@ -1637,120 +1320,90 @@ static void reader_flash() reader_repaint(); } -/** - Characters that may not be part of a token that is to be replaced - by a case insensitive completion. - */ +/// Characters that may not be part of a token that is to be replaced by a case insensitive +/// completion. #define REPLACE_UNCLEAN L"$*?({})" -/** - Check if the specified string can be replaced by a case insensitive - completion with the specified flags. +/// Check if the specified string can be replaced by a case insensitive completion with the +/// specified flags. +/// +/// Advanced tokens like those containing {}-style expansion can not at the moment be replaced, +/// other than if the new token is already an exact replacement, e.g. if the COMPLETE_DONT_ESCAPE +/// flag is set. +static bool reader_can_replace(const wcstring &in, int flags) { + const wchar_t *str = in.c_str(); - Advanced tokens like those containing {}-style expansion can not at - the moment be replaced, other than if the new token is already an - exact replacement, e.g. if the COMPLETE_DONT_ESCAPE flag is set. - */ - -static bool reader_can_replace(const wcstring &in, int flags) -{ - - const wchar_t * str = in.c_str(); - - if (flags & COMPLETE_DONT_ESCAPE) - { + 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; + + // Test characters that have a special meaning in any character position. + while (*str) { + if (wcschr(REPLACE_UNCLEAN, *str)) return false; str++; } return true; } -/* Determine the best match type for a set of completions */ -static fuzzy_match_type_t get_best_match_type(const std::vector &comp) -{ +/// Determine the best match type for a set of completions. +static fuzzy_match_type_t get_best_match_type(const std::vector &comp) { fuzzy_match_type_t best_type = fuzzy_match_none; - for (size_t i=0; i < comp.size(); i++) - { + for (size_t i = 0; i < comp.size(); i++) { best_type = std::min(best_type, comp.at(i).match.type); } - /* If the best type is an exact match, reduce it to prefix match. Otherwise a tab completion will only show one match if it matches a file exactly. (see issue #959) */ - if (best_type == fuzzy_match_exact) - { + // If the best type is an exact match, reduce it to prefix match. Otherwise a tab completion + // will only show one match if it matches a file exactly. (see issue #959). + if (best_type == fuzzy_match_exact) { best_type = fuzzy_match_prefix; } return best_type; } -/** - 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 - space. - - If the list contains multiple elements with a common prefix, write - 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 - \param continue_after_prefix_insertion If we have a shared prefix, whether to print the list of completions after inserting it. - - Return true if we inserted text into the command line, false if we did not. -*/ - -static bool handle_completions(const std::vector &comp, bool continue_after_prefix_insertion) -{ +/// 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 space. +/// - If the list contains multiple elements with a common prefix, write 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 +/// \param continue_after_prefix_insertion If we have a shared prefix, whether to print the list of +/// completions after inserting it. +/// +/// Return true if we inserted text into the command line, false if we did not. +static bool handle_completions(const std::vector &comp, + bool continue_after_prefix_insertion) { bool done = false; bool success = false; const editable_line_t *el = &data->command_line; const wchar_t *begin, *end, *buff = el->text.c_str(); parse_util_token_extent(buff, el->position, &begin, 0, 0, 0); - end = buff+el->position; + end = buff + el->position; const wcstring tok(begin, end - begin); - /* - Check trivial cases - */ - switch (comp.size()) - { - /* No suitable completions found, flash screen and return */ - case 0: - { + // Check trivial cases. + switch (comp.size()) { + case 0: { + // No suitable completions found, flash screen and return. reader_flash(); done = true; success = false; break; } - - /* Exactly one suitable completion found - insert it */ - case 1: - { - + case 1: { + // Exactly one suitable completion found - insert it. 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.flags & COMPLETE_REPLACES_TOKEN) || reader_can_replace(tok, c.flags)) - { + // 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.flags & COMPLETE_REPLACES_TOKEN) || reader_can_replace(tok, c.flags)) { completion_insert(c.completion.c_str(), c.flags); } done = true; @@ -1759,103 +1412,90 @@ static bool handle_completions(const std::vector &comp, bool conti } } - - if (!done) - { + if (!done) { fuzzy_match_type_t best_match_type = get_best_match_type(comp); - /* Determine whether we are going to replace the token or not. If any commands of the best type do not require replacement, then ignore all those that want to use replacement */ + // Determine whether we are going to replace the token or not. If any commands of the best + // type do not require replacement, then ignore all those that want to use replacement. bool will_replace_token = true; - for (size_t i=0; i< comp.size(); i++) - { + for (size_t i = 0; i < comp.size(); i++) { const completion_t &el = comp.at(i); - if (el.match.type <= best_match_type && !(el.flags & COMPLETE_REPLACES_TOKEN)) - { + if (el.match.type <= best_match_type && !(el.flags & COMPLETE_REPLACES_TOKEN)) { will_replace_token = false; break; } } - /* Decide which completions survived. There may be a lot of them; it would be nice if we could figure out how to avoid copying them here */ + // Decide which completions survived. There may be a lot of them; it would be nice if we + // could figure out how to avoid copying them here. std::vector surviving_completions; - for (size_t i=0; i < comp.size(); i++) - { + for (size_t i = 0; i < comp.size(); i++) { const completion_t &el = comp.at(i); - /* Ignore completions with a less suitable match type than the best. */ - if (el.match.type > best_match_type) - continue; + // Ignore completions with a less suitable match type than the best. + if (el.match.type > best_match_type) continue; - /* Only use completions that match replace_token */ + // Only use completions that match replace_token. bool completion_replace_token = !!(el.flags & COMPLETE_REPLACES_TOKEN); - if (completion_replace_token != will_replace_token) - continue; + if (completion_replace_token != will_replace_token) continue; - /* Don't use completions that want to replace, if we cannot replace them */ - if (completion_replace_token && ! reader_can_replace(tok, el.flags)) - continue; + // Don't use completions that want to replace, if we cannot replace them. + if (completion_replace_token && !reader_can_replace(tok, el.flags)) continue; - /* This completion survived */ + // This completion survived. surviving_completions.push_back(el); } - - /* Try to find a common prefix to insert among the surviving completions */ + // Try to find a common prefix to insert among the surviving completions. wcstring common_prefix; complete_flags_t flags = 0; bool prefix_is_partial_completion = false; - for (size_t i=0; i < surviving_completions.size(); i++) - { + for (size_t i = 0; i < surviving_completions.size(); i++) { const completion_t &el = surviving_completions.at(i); - if (i == 0) - { - /* First entry, use the whole string */ + if (i == 0) { + // First entry, use the whole string. common_prefix = el.completion; flags = el.flags; - } - else - { - /* Determine the shared prefix length. */ + } else { + // Determine the shared prefix length. size_t idx, max = mini(common_prefix.size(), el.completion.size()); - for (idx=0; idx < max; idx++) - { + for (idx = 0; idx < max; idx++) { wchar_t ac = common_prefix.at(idx), bc = el.completion.at(idx); bool matches = (ac == bc); - /* If we are replacing the token, allow case to vary */ - if (will_replace_token && ! matches) - { - /* Hackish way to compare two strings in a case insensitive way, hopefully better than towlower(). */ + // If we are replacing the token, allow case to vary. + if (will_replace_token && !matches) { + // Hackish way to compare two strings in a case insensitive way, hopefully + // better than towlower(). matches = (wcsncasecmp(&ac, &bc, 1) == 0); } - if (! matches) - break; + if (!matches) break; } - /* idx is now the length of the new common prefix */ + // idx is now the length of the new common prefix. common_prefix.resize(idx); prefix_is_partial_completion = true; - /* Early out if we decide there's no common prefix */ - if (idx == 0) - break; + // Early out if we decide there's no common prefix. + if (idx == 0) break; } } - /* Determine if we use the prefix. We use it if it's non-empty and it will actually make the command line longer. It may make the command line longer by virtue of not using REPLACE_TOKEN (so it always appends to the command line), or by virtue of replacing the token but being longer than it. */ + // Determine if we use the prefix. We use it if it's non-empty and it will actually make the + // command line longer. It may make the command line longer by virtue of not using + // REPLACE_TOKEN (so it always appends to the command line), or by virtue of replacing the + // token but being longer than it. bool use_prefix = common_prefix.size() > (will_replace_token ? tok.size() : 0); - assert(! use_prefix || ! common_prefix.empty()); + assert(!use_prefix || !common_prefix.empty()); - if (use_prefix) - { - /* We got something. If more than one completion contributed, then it means we have a prefix; don't insert a space after it */ - if (prefix_is_partial_completion) - flags |= COMPLETE_NO_SPACE; + if (use_prefix) { + // We got something. If more than one completion contributed, then it means we have a + // prefix; don't insert a space after it. + if (prefix_is_partial_completion) flags |= COMPLETE_NO_SPACE; completion_insert(common_prefix.c_str(), flags); success = true; } - if (continue_after_prefix_insertion || ! use_prefix) - { - /* We didn't get a common prefix, or we want to print the list anyways. */ + if (continue_after_prefix_insertion || !use_prefix) { + // We didn't get a common prefix, or we want to print the list anyways. size_t len, prefix_start = 0; wcstring prefix; parse_util_get_parameter_info(el->text, el->position, NULL, &prefix_start, NULL); @@ -1863,173 +1503,140 @@ static bool handle_completions(const std::vector &comp, bool conti assert(el->position >= prefix_start); len = el->position - prefix_start; - if (will_replace_token || match_type_requires_full_replacement(best_match_type)) - { - // No prefix + if (will_replace_token || match_type_requires_full_replacement(best_match_type)) { + // No prefix. prefix.clear(); - } - else if (len <= PREFIX_MAX_LEN) - { + } else if (len <= PREFIX_MAX_LEN) { prefix.append(el->text, prefix_start, len); - } - else - { - // append just the end of the string + } else { + // Append just the end of the string. prefix = wcstring(&ellipsis_char, 1); prefix.append(el->text, prefix_start + len - PREFIX_MAX_LEN, PREFIX_MAX_LEN); } wchar_t quote; parse_util_get_parameter_info(el->text, el->position, "e, NULL, NULL); - - /* Update the pager data */ + // Update the pager data. data->pager.set_prefix(prefix); data->pager.set_completions(surviving_completions); - - /* Invalidate our rendering */ + // Invalidate our rendering. data->current_page_rendering = page_rendering_t(); - - /* Modify the command line to reflect the new pager */ + // Modify the command line to reflect the new pager. data->pager_selection_changed(); - reader_repaint_needed(); - success = false; } } return success; } -/* Return true if we believe ourselves to be orphaned. loop_count is how many times we've tried to stop ourselves via SIGGTIN */ -static bool check_for_orphaned_process(unsigned long loop_count, pid_t shell_pgid) -{ +/// Return true if we believe ourselves to be orphaned. loop_count is how many times we've tried to +/// stop ourselves via SIGGTIN. +static bool check_for_orphaned_process(unsigned long loop_count, pid_t shell_pgid) { bool we_think_we_are_orphaned = false; - /* Try kill-0'ing the process whose pid corresponds to our process group ID. It's possible this will fail because we don't have permission to signal it. But more likely it will fail because it no longer exists, and we are orphaned. */ - if (loop_count % 64 == 0) - { - if (kill(shell_pgid, 0) < 0 && errno == ESRCH) - { + // Try kill-0'ing the process whose pid corresponds to our process group ID. It's possible this + // will fail because we don't have permission to signal it. But more likely it will fail because + // it no longer exists, and we are orphaned. + if (loop_count % 64 == 0) { + if (kill(shell_pgid, 0) < 0 && errno == ESRCH) { we_think_we_are_orphaned = true; } } - if (! we_think_we_are_orphaned && loop_count % 128 == 0) - { - /* Try reading from the tty; if we get EIO we are orphaned. This is sort of bad because it may block. */ - + if (!we_think_we_are_orphaned && loop_count % 128 == 0) { + // Try reading from the tty; if we get EIO we are orphaned. This is sort of bad because it + // may block. char *tty = ctermid(NULL); - if (! tty) - { + if (!tty) { wperror(L"ctermid"); exit_without_destructors(1); } - /* Open the tty. Presumably this is stdin, but maybe not? */ + // Open the tty. Presumably this is stdin, but maybe not? int tty_fd = open(tty, O_RDONLY | O_NONBLOCK); - if (tty_fd < 0) - { + if (tty_fd < 0) { wperror(L"open"); exit_without_destructors(1); } char tmp; - if (read(tty_fd, &tmp, 1) < 0 && errno == EIO) - { + if (read(tty_fd, &tmp, 1) < 0 && errno == EIO) { we_think_we_are_orphaned = true; } close(tty_fd); } - /* Just give up if we've done it a lot times */ - if (loop_count > 4096) - { + // Just give up if we've done it a lot times. + if (loop_count > 4096) { we_think_we_are_orphaned = true; } return we_think_we_are_orphaned; } -/** - Initialize data for interactive use -*/ -static void reader_interactive_init() -{ - /* See if we are running interactively. */ +/// Initialize data for interactive use. +static void reader_interactive_init() { + // See if we are running interactively. pid_t shell_pgid; 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(STDIN_FILENO) != shell_pgid) - { + // 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(STDIN_FILENO) != 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. - - 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()) - { + // 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. + // + // 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. However, the call to signal(SIGTTIN) may silently not do anything if we are orphaned. - - As far as I can tell there's no really good way to detect that we are orphaned. One way is to just detect if the group leader exited, via kill(shell_pgid, 0). Another possibility is that read() from the tty fails with EIO - this is more reliable but it's harder, because it may succeed or block. So we loop for a while, trying those strategies. Eventually we just give up and assume we're orphaend. - */ - for (unsigned long loop_count = 0;; loop_count++) - { + // Ok, signal handlers are taken out of the picture. Stop ourself in a loop until we are in + // control of the terminal. However, the call to signal(SIGTTIN) may silently not do + // anything if we are orphaned. + // + // As far as I can tell there's no really good way to detect that we are orphaned. One way + // is to just detect if the group leader exited, via kill(shell_pgid, 0). Another + // possibility is that read() from the tty fails with EIO - this is more reliable but it's + // harder, because it may succeed or block. So we loop for a while, trying those strategies. + // Eventually we just give up and assume we're orphaend. + for (unsigned long loop_count = 0;; loop_count++) { pid_t owner = tcgetpgrp(STDIN_FILENO); shell_pgid = getpgrp(); - if (owner < 0 && errno == ENOTTY) - { + if (owner < 0 && errno == ENOTTY) { // No TTY, cannot be interactive? - debug(1, - _(L"No TTY for interactive shell (tcgetpgrp failed)")); + debug(1, _(L"No TTY for interactive shell (tcgetpgrp failed)")); wperror(L"setpgid"); exit_without_destructors(1); } - if (owner == shell_pgid) - { - /* Success */ - break; - } - else - { - if (check_for_orphaned_process(loop_count, shell_pgid)) - { - /* We're orphaned, so we just die. Another sad statistic. */ - debug(1, - _(L"I appear to be an orphaned process, so I am quitting politely. My pid is %d."), (int)getpid()); + if (owner == shell_pgid) { + break; // success + } else { + if (check_for_orphaned_process(loop_count, shell_pgid)) { + // We're orphaned, so we just die. Another sad statistic. + debug(1, _(L"I appear to be an orphaned process, so I am quitting politely. My " + L"pid is %d."), + (int)getpid()); exit_without_destructors(1); } - /* Try stopping us */ + // Try stopping us. int ret = killpg(shell_pgid, SIGTTIN); - if (ret < 0) - { + if (ret < 0) { wperror(L"killpg"); exit_without_destructors(1); } @@ -2038,40 +1645,31 @@ static void reader_interactive_init() signal_set_handlers(); - for (i=0; icommand_line.position > data->command_line.size()) - sanity_lose(); - - if (data->colors.size() != data->command_line.size()) - sanity_lose(); - - if (data->indents.size() != data->command_line.size()) - sanity_lose(); - +void reader_sanity_check() { + // Note: 'data' is non-null if we are interactive, except in the testing environment. + if (get_is_interactive() && data != NULL) { + if (data->command_line.position > data->command_line.size()) sanity_lose(); + if (data->colors.size() != data->command_line.size()) sanity_lose(); + if (data->indents.size() != data->command_line.size()) sanity_lose(); } } -/** - Set the specified string as the current buffer. -*/ -static void set_command_line_and_position(editable_line_t *el, const wcstring &new_str, size_t pos) -{ +/// Set the specified string as the current buffer. +static void set_command_line_and_position(editable_line_t *el, const wcstring &new_str, + size_t pos) { el->text = new_str; update_buff_pos(el, pos); data->command_line_changed(el); @@ -2120,94 +1704,71 @@ static void set_command_line_and_position(editable_line_t *el, const wcstring &n reader_repaint_needed(); } -static void reader_replace_current_token(const wcstring &new_token) -{ - +static void reader_replace_current_token(const wcstring &new_token) { const wchar_t *begin, *end; size_t new_pos; - /* Find current token */ + // Find current token. editable_line_t *el = data->active_edit_line(); const wchar_t *buff = el->text.c_str(); parse_util_token_extent(buff, el->position, &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) + new_token.size(); + new_pos = (begin - buff) + new_token.size(); set_command_line_and_position(el, new_buff, new_pos); } - -/** - Reset the data structures associated with the token search -*/ -static void reset_token_history() -{ +/// Reset the data structures associated with the token search. +static void reset_token_history() { const editable_line_t *el = data->active_edit_line(); const wchar_t *begin, *end; const wchar_t *buff = el->text.c_str(); parse_util_token_extent((wchar_t *)buff, el->position, &begin, &end, 0, 0); data->search_buff.clear(); - if (begin) - { + if (begin) { data->search_buff.append(begin, end - begin); } data->token_history_pos = -1; - data->search_pos=0; + 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); + data->history_search = + history_search_t(*data->history, data->search_buff, HISTORY_SEARCH_TYPE_CONTAINS); } - -/** - Handles a token search command. - - \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) -{ - /* Paranoia */ - if (! data) - return; +/// Handles a token search command. +/// +/// \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) { + if (!data) return; wcstring str; size_t current_pos; - if (reset) - { - /* - Start a new token search using the current token - */ + 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) - { + 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); - } - else - { + } else { data->search_pos++; str = data->search_prev.at(data->search_pos); } @@ -2215,192 +1776,139 @@ static void handle_token_history(int forward, int reset) reader_replace_current_token(str); reader_super_highlight_me_plenty(); reader_repaint(); - } - else - { - if (current_pos == size_t(-1)) - { + } else { + if (current_pos == size_t(-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()) { data->token_history_buff = data->history_search.current_string(); } 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) - { + if (data->search_buff != last) { str = data->search_buff; - } - else - { + } else { return; } - } - else - { - - //debug( 3, L"new '%ls'", data->token_history_buff.c_str() ); + } else { + // debug( 3, L"new '%ls'", data->token_history_buff.c_str() ); tokenizer_t tok(data->token_history_buff.c_str(), TOK_ACCEPT_UNFINISHED); tok_t token; - while (tok.next(&token)) - { - switch (token.type) - { - case TOK_STRING: - { - if (token.text.find(data->search_buff) != wcstring::npos) - { - //debug( 3, L"Found token at pos %d\n", tok_get_pos( &tok ) ); - if (token.offset >= current_pos) - { + while (tok.next(&token)) { + switch (token.type) { + case TOK_STRING: { + if (token.text.find(data->search_buff) != wcstring::npos) { + // debug( 3, L"Found token at pos %d\n", tok_get_pos( &tok ) ); + if (token.offset >= current_pos) { break; } - //debug( 3, L"ok pos" ); + // debug( 3, L"ok pos" ); - if (find(data->search_prev.begin(), data->search_prev.end(), token.text) == data->search_prev.end()) - { + if (find(data->search_prev.begin(), data->search_prev.end(), + token.text) == data->search_prev.end()) { data->token_history_pos = token.offset; str = token.text; } - } - } - break; - - default: - { break; } - + default: { break; } } } } - if (!str.empty()) - { + if (!str.empty()) { reader_replace_current_token(str); reader_super_highlight_me_plenty(); reader_repaint(); data->search_pos = data->search_prev.size(); data->search_prev.push_back(str); - } - else if (! reader_interrupted()) - { - data->token_history_pos=-1; + } else if (!reader_interrupted()) { + data->token_history_pos = -1; handle_token_history(0, 0); } } } -/** - Move buffer position one word or erase one word. This function - updates both the internal buffer and the screen. It is used by - M-left, M-right and ^W to do block movement or block erase. +/// Move buffer position one word or erase one word. This function updates both the internal buffer +/// and the screen. It is used by M-left, M-right and ^W to do block movement or block erase. +/// +/// \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. +enum move_word_dir_t { MOVE_DIR_LEFT, MOVE_DIR_RIGHT }; - \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. -*/ -enum move_word_dir_t -{ - MOVE_DIR_LEFT, - MOVE_DIR_RIGHT -}; - -static void move_word(editable_line_t *el, bool move_right, bool erase, enum move_word_style_t style, bool newv) -{ - /* Return if we are already at the edge */ +static void move_word(editable_line_t *el, bool move_right, bool erase, + enum move_word_style_t style, bool newv) { + // Return if we are already at the edge. const size_t boundary = move_right ? el->size() : 0; - if (el->position == boundary) - return; + if (el->position == boundary) return; - /* When moving left, a value of 1 means the character at index 0. */ + // When moving left, a value of 1 means the character at index 0. move_word_state_machine_t state(style); - const wchar_t * const command_line = el->text.c_str(); + const wchar_t *const command_line = el->text.c_str(); const size_t start_buff_pos = el->position; size_t buff_pos = el->position; - while (buff_pos != boundary) - { + while (buff_pos != boundary) { size_t idx = (move_right ? buff_pos : buff_pos - 1); wchar_t c = command_line[idx]; - if (! state.consume_char(c)) - break; + if (!state.consume_char(c)) break; buff_pos = (move_right ? buff_pos + 1 : buff_pos - 1); } - /* Always consume at least one character */ - if (buff_pos == start_buff_pos) - buff_pos = (move_right ? buff_pos + 1 : buff_pos - 1); + // Always consume at least one character. + if (buff_pos == start_buff_pos) buff_pos = (move_right ? buff_pos + 1 : buff_pos - 1); - /* If we are moving left, buff_pos-1 is the index of the first character we do not delete (possibly -1). If we are moving right, then buff_pos is that index - possibly el->size(). */ - if (erase) - { - /* Don't autosuggest after a kill */ - if (el == &data->command_line) - { + // If we are moving left, buff_pos-1 is the index of the first character we do not delete + // (possibly -1). If we are moving right, then buff_pos is that index - possibly el->size(). + if (erase) { + // Don't autosuggest after a kill. + if (el == &data->command_line) { data->suppress_autosuggestion = true; } - if (move_right) - { + if (move_right) { reader_kill(el, start_buff_pos, buff_pos - start_buff_pos, KILL_APPEND, newv); - } - else - { + } else { reader_kill(el, buff_pos, start_buff_pos - buff_pos, KILL_PREPEND, newv); } - } - else - { + } else { update_buff_pos(el, buff_pos); reader_repaint(); } - } -const wchar_t *reader_get_buffer(void) -{ +const wchar_t *reader_get_buffer(void) { ASSERT_IS_MAIN_THREAD(); return data ? data->command_line.text.c_str() : NULL; } -history_t *reader_get_history(void) -{ +history_t *reader_get_history(void) { ASSERT_IS_MAIN_THREAD(); return data ? data->history : NULL; } -/* Sets the command line contents, without clearing the pager */ -static void reader_set_buffer_maintaining_pager(const wcstring &b, size_t pos) -{ - /* 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. */ +/// Sets the command line contents, without clearing the pager. +static void reader_set_buffer_maintaining_pager(const wcstring &b, size_t pos) { + // 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(); data->command_line.text = b; data->command_line_changed(&data->command_line); - /* Don't set a position past the command line length */ - if (pos > command_line_len) - pos = command_line_len; + // Don't set a position past the command line length. + if (pos > command_line_len) pos = command_line_len; update_buff_pos(&data->command_line, pos); - /* Clear history search and pager contents */ + // Clear history search and pager contents. data->search_mode = NO_SEARCH; data->search_buff.clear(); data->history_search.go_to_end(); @@ -2409,30 +1917,23 @@ static void reader_set_buffer_maintaining_pager(const wcstring &b, size_t pos) reader_repaint_needed(); } -/* Sets the command line contents, clearing the pager */ -void reader_set_buffer(const wcstring &b, size_t pos) -{ - if (!data) - return; +/// Sets the command line contents, clearing the pager. +void reader_set_buffer(const wcstring &b, size_t pos) { + if (!data) return; clear_pager(); reader_set_buffer_maintaining_pager(b, pos); } - -size_t reader_get_cursor_pos() -{ - if (!data) - return (size_t)(-1); +size_t reader_get_cursor_pos() { + if (!data) return (size_t)(-1); return data->command_line.position; } -bool reader_get_selection(size_t *start, size_t *len) -{ +bool reader_get_selection(size_t *start, size_t *len) { bool result = false; - if (data != NULL && data->sel_active) - { + if (data != NULL && data->sel_active) { *start = data->sel_start_pos; *len = std::min(data->sel_stop_pos - data->sel_start_pos + 1, data->command_line.size()); result = true; @@ -2440,17 +1941,14 @@ bool reader_get_selection(size_t *start, size_t *len) return result; } - #define ENV_CMD_DURATION L"CMD_DURATION" -void set_env_cmd_duration(struct timeval *after, struct timeval *before) -{ +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]; - if (after->tv_usec < before->tv_usec) - { + if (after->tv_usec < before->tv_usec) { usecs += 1000000; secs -= 1; } @@ -2459,15 +1957,12 @@ void set_env_cmd_duration(struct timeval *after, struct timeval *before) env_set(ENV_CMD_DURATION, buf, ENV_UNEXPORT); } -void reader_run_command(parser_t &parser, const wcstring &cmd) -{ - +void reader_run_command(parser_t &parser, const wcstring &cmd) { struct timeval time_before, time_after; wcstring ft = tok_first(cmd); - if (! ft.empty()) - env_set(L"_", ft.c_str(), ENV_GLOBAL); + if (!ft.empty()) env_set(L"_", ft.c_str(), ENV_GLOBAL); reader_write_title(cmd); @@ -2488,30 +1983,26 @@ void reader_run_command(parser_t &parser, const wcstring &cmd) #ifdef HAVE__PROC_SELF_STAT proc_update_jiffies(); #endif - - } - -parser_test_error_bits_t reader_shell_test(const wchar_t *b) -{ +parser_test_error_bits_t reader_shell_test(const wchar_t *b) { assert(b != NULL); wcstring bstr = b; - /* Append a newline, to act as a statement terminator */ + // Append a newline, to act as a statement terminator. bstr.push_back(L'\n'); parse_error_list_t errors; - parser_test_error_bits_t res = parse_util_detect_errors(bstr, &errors, true /* do accept incomplete */); + parser_test_error_bits_t res = + parse_util_detect_errors(bstr, &errors, true /* do accept incomplete */); - if (res & PARSER_TEST_ERROR) - { + if (res & PARSER_TEST_ERROR) { wcstring error_desc; parser_t::principal_parser().get_backtrace(bstr, errors, &error_desc); - // ensure we end with a newline. Also add an initial newline, because it's likely the user just hit enter and so there's junk on the current line - if (! string_suffixes_string(L"\n", error_desc)) - { + // Ensure we end with a newline. Also add an initial newline, because it's likely the user + // just hit enter and so there's junk on the current line. + if (!string_suffixes_string(L"\n", error_desc)) { error_desc.push_back(L'\n'); } fwprintf(stderr, L"\n%ls", error_desc.c_str()); @@ -2519,30 +2010,22 @@ parser_test_error_bits_t reader_shell_test(const wchar_t *b) return res; } -/** - Test if the given string contains error. Since this is the error - detection for general purpose, there are no invalid strings, so - this function always returns false. -*/ -static parser_test_error_bits_t default_test(const wchar_t *b) -{ - return 0; -} +/// Test if the given string contains error. Since this is the error detection for general purpose, +/// there are no invalid strings, so this function always returns false. +static parser_test_error_bits_t default_test(const wchar_t *b) { 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->history = &history_t::history_with_name(name); n->app_name = name; n->next = data; - data=n; + data = n; data->command_line_changed(&data->command_line); - if (data->next == 0) - { + if (data->next == 0) { reader_interactive_init(); } @@ -2552,178 +2035,132 @@ void reader_push(const wchar_t *name) reader_set_left_prompt(L""); } -void reader_pop() -{ +void reader_pop() { reader_data_t *n = data; - if (data == 0) - { + 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 */ + // Invoke the destructor to balance our new. delete n; - if (data == 0) - { + if (data == 0) { reader_interactive_destroy(); - } - else - { + } else { end_loop = 0; - //history_set_mode( data->app_name.c_str() ); + // history_set_mode( data->app_name.c_str() ); s_reset(&data->screen, screen_reset_abandon_line); } } -void reader_set_left_prompt(const wcstring &new_prompt) -{ - data->left_prompt = new_prompt; -} +void reader_set_left_prompt(const wcstring &new_prompt) { data->left_prompt = new_prompt; } -void reader_set_right_prompt(const wcstring &new_prompt) -{ - data->right_prompt = new_prompt; -} +void reader_set_right_prompt(const wcstring &new_prompt) { data->right_prompt = new_prompt; } -void reader_set_allow_autosuggesting(bool flag) -{ - data->allow_autosuggestion = flag; -} +void reader_set_allow_autosuggesting(bool flag) { data->allow_autosuggestion = flag; } -void reader_set_expand_abbreviations(bool flag) -{ - data->expand_abbreviations = flag; -} +void reader_set_expand_abbreviations(bool flag) { data->expand_abbreviations = flag; } -void reader_set_complete_function(complete_function_t f) -{ - data->complete_func = f; -} +void reader_set_complete_function(complete_function_t f) { data->complete_func = f; } -void reader_set_highlight_function(highlight_function_t func) -{ - data->highlight_function = func; -} +void reader_set_highlight_function(highlight_function_t func) { data->highlight_function = func; } -void reader_set_test_function(parser_test_error_bits_t (*f)(const wchar_t *)) -{ +void reader_set_test_function(parser_test_error_bits_t (*f)(const wchar_t *)) { data->test_func = f; } -void reader_set_exit_on_interrupt(bool i) -{ - data->exit_on_interrupt = i; -} +void reader_set_exit_on_interrupt(bool i) { data->exit_on_interrupt = i; } -void reader_import_history_if_necessary(void) -{ - /* Import history from older location (config path) if our current history is empty */ - if (data->history && data->history->is_empty()) - { +void reader_import_history_if_necessary(void) { + // Import history from older location (config path) if our current history is empty. + if (data->history && data->history->is_empty()) { data->history->populate_from_config_path(); } - /* Import history from bash, etc. if our current history is still empty */ - if (data->history && data->history->is_empty()) - { - /* Try opening a bash file. We make an effort to respect $HISTFILE; this isn't very complete (AFAIK it doesn't have to be exported), and to really get this right we ought to ask bash itself. But this is better than nothing. - */ + // Import history from bash, etc. if our current history is still empty. + if (data->history && data->history->is_empty()) { + // Try opening a bash file. We make an effort to respect $HISTFILE; this isn't very complete + // (AFAIK it doesn't have to be exported), and to really get this right we ought to ask bash + // itself. But this is better than nothing. const env_var_t var = env_get_string(L"HISTFILE"); wcstring path = (var.missing() ? L"~/.bash_history" : var); expand_tilde(path); FILE *f = wfopen(path, "r"); - if (f) - { + if (f) { data->history->populate_from_bash(f); fclose(f); } } } -/** A class as the context pointer for a background (threaded) highlight operation. */ -class background_highlight_context_t -{ -public: - /** The string to highlight */ +/// A class as the context pointer for a background (threaded) highlight operation. +class background_highlight_context_t { + public: + /// The string to highlight. const wcstring string_to_highlight; - - /** Color buffer */ + /// Color buffer. std::vector colors; - - /** The position to use for bracket matching */ + /// The position to use for bracket matching. const size_t match_highlight_pos; - - /** Function for syntax highlighting */ + /// Function for syntax highlighting. const highlight_function_t highlight_function; - - /** Environment variables */ + /// Environment variables. const env_vars_snapshot_t vars; - - /** When the request was made */ + /// When the request was made. const double when; - - /** Gen count at the time the request was made */ + /// 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), - match_highlight_pos(phighlight_pos), - highlight_function(phighlight_func), - vars(env_vars_snapshot_t::highlighting_keys), - when(timef()), - generation_count(s_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), + match_highlight_pos(phighlight_pos), + highlight_function(phighlight_func), + vars(env_vars_snapshot_t::highlighting_keys), + when(timef()), + generation_count(s_generation_count) {} - int perform_highlight() - { - if (generation_count != s_generation_count) - { - // The gen count has changed, so don't do anything + int perform_highlight() { + if (generation_count != s_generation_count) { + // The gen count has changed, so don't do anything. return 0; } - if (! string_to_highlight.empty()) - { - highlight_function(string_to_highlight, colors, match_highlight_pos, NULL /* error */, vars); + if (!string_to_highlight.empty()) { + 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()) - { +/// 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()) { const editable_line_t *el = &data->command_line; const wcstring &needle = data->search_buff; size_t match_pos = el->text.find(needle); - if (match_pos != wcstring::npos) - { + if (match_pos != wcstring::npos) { size_t end = match_pos + needle.size(); - for (size_t i=match_pos; i < end; i++) - { - data->colors.at(i) |= (highlight_spec_search_match<<16); + for (size_t i = match_pos; i < end; i++) { + data->colors.at(i) |= (highlight_spec_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.text) - { - /* 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.text) { + // 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_line.size()); - if (data->colors != ctx->colors) - { + if (data->colors != ctx->colors) { data->colors.swap(ctx->colors); sanity_check(); highlight_search(); @@ -2731,28 +2168,24 @@ static void highlight_complete(background_highlight_context_t *ctx, int result) } } - /* 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->perform_highlight(); } - -/** - Call specified external highlighting function and then do search - highlighting. Lastly, clear the background color under the cursor - to avoid repaint issues on terminals where e.g. syntax highlighting - maykes characters under the sursor unreadable. - - \param match_highlight_pos_adjust the adjustment to the position to use for bracket matching. This is added to the current cursor position and may be negative. - \param error if non-null, any possible errors in the buffer are further descibed by the strings inserted into the specified arraylist - \param no_io if true, do a highlight that does not perform I/O, synchronously. If false, perform an asynchronous highlight in the background, which may perform disk I/O. -*/ -static void reader_super_highlight_me_plenty(int match_highlight_pos_adjust, bool no_io) -{ +/// Call specified external highlighting function and then do search highlighting. Lastly, clear the +/// background color under the cursor to avoid repaint issues on terminals where e.g. syntax +/// highlighting maykes characters under the sursor unreadable. +/// +/// \param match_highlight_pos_adjust the adjustment to the position to use for bracket matching. +/// This is added to the current cursor position and may be negative. +/// \param error if non-null, any possible errors in the buffer are further descibed by the strings +/// inserted into the specified arraylist +/// \param no_io if true, do a highlight that does not perform I/O, synchronously. If false, perform +/// an asynchronous highlight in the background, which may perform disk I/O. +static void reader_super_highlight_me_plenty(int match_highlight_pos_adjust, bool no_io) { const editable_line_t *el = &data->command_line; long match_highlight_pos = (long)el->position + match_highlight_pos_adjust; assert(match_highlight_pos >= 0); @@ -2760,103 +2193,77 @@ static void reader_super_highlight_me_plenty(int match_highlight_pos_adjust, boo reader_sanity_check(); highlight_function_t highlight_func = no_io ? highlight_shell_no_io : data->highlight_function; - background_highlight_context_t *ctx = new background_highlight_context_t(el->text, match_highlight_pos, highlight_func); - if (no_io) - { - // Highlighting without IO, we just do it - // Note that highlight_complete deletes ctx. + background_highlight_context_t *ctx = + new background_highlight_context_t(el->text, match_highlight_pos, highlight_func); + if (no_io) { + // Highlighting without IO, we just do it. Note that highlight_complete deletes ctx. int result = ctx->perform_highlight(); highlight_complete(ctx, result); - } - else - { - // Highlighting including I/O proceeds in the background + } else { + // Highlighting including I/O proceeds in the background. 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. */ + // 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 = el->text, &suggest = data->autosuggestion; - if (can_autosuggest() && ! suggest.empty() && string_prefixes_string_case_insensitive(cmd, suggest)) - { - /* The autosuggestion is still reasonable, so do nothing */ - } - else - { + if (can_autosuggest() && !suggest.empty() && + string_prefixes_string_case_insensitive(cmd, suggest)) { + // The autosuggestion is still reasonable, so do nothing. + } else { update_autosuggestion(); } } - -bool shell_is_exiting() -{ +bool shell_is_exiting() { if (get_is_interactive()) return job_list_is_empty() && data != NULL && data->end_loop; else return end_loop; } -/** - This function is called when the main loop notices that end_loop - has been set while in interactive mode. It checks if it is ok to - exit. - */ - -static void handle_end_loop() -{ +/// This function is called when the main loop notices that end_loop has been set while in +/// interactive mode. It checks if it is ok to exit. +static void handle_end_loop() { job_t *j; - int stopped_jobs_count=0; - int is_breakpoint=0; + int stopped_jobs_count = 0; + int is_breakpoint = 0; const parser_t &parser = parser_t::principal_parser(); - for (size_t i = 0; i < parser.block_count(); i++) - { - if (parser.block_at_index(i)->type() == BREAKPOINT) - { + for (size_t i = 0; i < parser.block_count(); i++) { + if (parser.block_at_index(i)->type() == BREAKPOINT) { is_breakpoint = 1; break; } } job_iterator_t jobs; - while ((j = jobs.next())) - { - if (!job_is_completed(j) && (job_is_stopped(j))) - { + while ((j = jobs.next())) { + if (!job_is_completed(j) && (job_is_stopped(j))) { stopped_jobs_count++; break; } } - if (!reader_exit_forced() && !data->prev_end_loop && stopped_jobs_count && !is_breakpoint) - { - writestr(_(L"There are stopped jobs. A second attempt to exit will enforce their termination.\n")); + if (!reader_exit_forced() && !data->prev_end_loop && stopped_jobs_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. - */ + 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 issue #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())) - { - /* Send SIGHUP only to foreground processes. - - See https://github.com/fish-shell/fish-shell/issues/1771 - */ - if (! job_is_completed(j) && job_get_flag(j, JOB_FOREGROUND)) - { + while ((j = jobs.next())) { + // Send SIGHUP only to foreground processes. See issue #1771. + if (!job_is_completed(j) && job_get_flag(j, JOB_FOREGROUND)) { job_signal(j, SIGHUP); } } @@ -2864,27 +2271,19 @@ static void handle_end_loop() } } -static bool selection_is_at_top() -{ +static bool selection_is_at_top() { const pager_t *pager = &data->pager; size_t row = pager->get_selected_row(data->current_page_rendering); - if (row != 0 && row != PAGER_SELECTION_NONE) - return false; + if (row != 0 && row != PAGER_SELECTION_NONE) return false; size_t col = pager->get_selected_column(data->current_page_rendering); - if (col != 0 && col != PAGER_SELECTION_NONE) - return false; + if (col != 0 && col != PAGER_SELECTION_NONE) return false; return true; } - -/** - Read interactively. Read input from stdin while providing editing - facilities. -*/ -static int read_i(void) -{ +/// Read interactively. Read input from stdin while providing editing facilities. +static int read_i(void) { reader_push(L"fish"); reader_set_complete_function(&complete); reader_set_highlight_function(&highlight_shell); @@ -2895,10 +2294,9 @@ static int read_i(void) parser_t &parser = parser_t::principal_parser(); - data->prev_end_loop=0; + data->prev_end_loop = 0; - while ((!data->end_loop) && (!sanity_check())) - { + while ((!data->end_loop) && (!sanity_check())) { event_fire_generic(L"fish_prompt"); if (function_exists(LEFT_PROMPT_FUNCTION_NAME)) reader_set_left_prompt(LEFT_PROMPT_FUNCTION_NAME); @@ -2910,21 +2308,13 @@ static int read_i(void) 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. - */ - + // Put buff in temporary string and clear buff, so that we can handle a call to + // reader_set_buffer during evaluation. const wchar_t *tmp = reader_readline(0); - if (data->end_loop) - { + if (data->end_loop) { handle_end_loop(); - } - else if (tmp) - { + } else if (tmp) { const wcstring command = tmp; update_buff_pos(&data->command_line, 0); data->command_line.text.clear(); @@ -2933,34 +2323,24 @@ static int read_i(void) event_fire_generic(L"fish_preexec", &argv); reader_run_command(parser, command); event_fire_generic(L"fish_postexec", &argv); - /* Allow any pending history items to be returned in the history array. */ - if (data->history) - { + // Allow any pending history items to be returned in the history array. + if (data->history) { data->history->resolve_pending(); } - if (data->end_loop) - { + if (data->end_loop) { handle_end_loop(); - } - else - { - data->prev_end_loop=0; + } else { + data->prev_end_loop = 0; } } - - } reader_pop(); return 0; } -/** - Test if there are bytes available for reading on the specified file - descriptor -*/ -static int can_read(int fd) -{ - struct timeval can_read_timeout = { 0, 0 }; +/// Test if there are bytes available for reading on the specified file descriptor. +static int can_read(int fd) { + struct timeval can_read_timeout = {0, 0}; fd_set fds; FD_ZERO(&fds); @@ -2968,85 +2348,71 @@ static int can_read(int fd) return select(fd + 1, &fds, 0, 0, &can_read_timeout) == 1; } -// Test if the specified character is in a range that fish uses interally to -// store special tokens. +// Test if the specified character is in a range that fish uses interally to store special tokens. // -// NOTE: This is used when tokenizing the input. It is also used when reading -// input, before tokenization, to replace such chars with REPLACEMENT_WCHAR if -// they're not part of a quoted string. We don't want external input to be able -// to feed reserved characters into our lexer/parser or code evaluator. +// NOTE: This is used when tokenizing the input. It is also used when reading input, before +// tokenization, to replace such chars with REPLACEMENT_WCHAR if they're not part of a quoted +// string. We don't want external input to be able to feed reserved characters into our lexer/parser +// or code evaluator. // // TODO: Actually implement the replacement as documented above. -static int wchar_private(wchar_t c) -{ +static int wchar_private(wchar_t c) { return ((c >= RESERVED_CHAR_BASE && c < RESERVED_CHAR_END) || (c >= ENCODE_DIRECT_BASE && c < ENCODE_DIRECT_END) || (c >= INPUT_COMMON_BASE && c < INPUT_COMMON_END)); } -/** - Test if the specified character in the specified string is - backslashed. pos may be at the end of the string, which indicates - if there is a trailing backslash. -*/ -static bool is_backslashed(const wcstring &str, size_t pos) -{ - /* note pos == str.size() is OK */ - if (pos > str.size()) - return false; +/// Test if the specified character in the specified string is backslashed. pos may be at the end of +/// the string, which indicates if there is a trailing backslash. +static bool is_backslashed(const wcstring &str, size_t pos) { + // note pos == str.size() is OK. + if (pos > str.size()) return false; size_t count = 0, idx = pos; - while (idx--) - { - if (str.at(idx) != L'\\') - break; + while (idx--) { + if (str.at(idx) != L'\\') break; count++; } return (count % 2) == 1; } -static wchar_t unescaped_quote(const wcstring &str, size_t pos) -{ +static wchar_t unescaped_quote(const wcstring &str, size_t pos) { wchar_t result = L'\0'; - if (pos < str.size()) - { + if (pos < str.size()) { wchar_t c = str.at(pos); - if ((c == L'\'' || c == L'"') && ! is_backslashed(str, pos)) - { + if ((c == L'\'' || c == L'"') && !is_backslashed(str, pos)) { result = c; } } return result; } -/* Returns true if the last token is a comment. */ -static bool text_ends_in_comment(const wcstring &text) -{ +/// Returns true if the last token is a comment. +static bool text_ends_in_comment(const wcstring &text) { tokenizer_t tok(text.c_str(), TOK_ACCEPT_UNFINISHED | TOK_SHOW_COMMENTS | TOK_SQUASH_ERRORS); tok_t token; - while (tok.next(&token)) - { + while (tok.next(&token)) { // pass } return token.type == TOK_COMMENT; } -const wchar_t *reader_readline(int nchars) -{ +const wchar_t *reader_readline(int nchars) { wint_t c; - int last_char=0; - size_t yank_len=0; + int last_char = 0; + size_t yank_len = 0; const wchar_t *yank_str; bool comp_empty = true; std::vector comp; - int finished=0; + int finished = 0; struct termios old_modes; - /* Coalesce redundant repaints. When we get a repaint, we set this to true, and skip repaints until we get something else. */ + // Coalesce redundant repaints. When we get a repaint, we set this to true, and skip repaints + // until we get something else. bool coalescing_repaints = false; - /* The command line before completion */ + // The command line before completion. data->cycle_command_line.clear(); data->cycle_cursor_pos = 0; @@ -3059,188 +2425,139 @@ const wchar_t *reader_readline(int nchars) s_reset(&data->screen, screen_reset_abandon_line); 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)) - { + // 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"); } - while (!finished && !data->end_loop) - { - if (0 < nchars && (size_t)nchars <= data->command_line.size()) - { - // we've already hit the specified character limit + while (!finished && !data->end_loop) { + if (0 < nchars && (size_t)nchars <= data->command_line.size()) { + // We've already hit the specified character limit. finished = 1; break; } - /* - 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) - { + // 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(); + c = input_readch(); is_interactive_read = was_interactive_read; - //fprintf(stderr, "C: %lx\n", (long)c); + // fprintf(stderr, "C: %lx\n", (long)c); - if (((!wchar_private(c))) && (c>31) && (c != 127)) - { - if (can_read(0)) - { - - wchar_t arr[READAHEAD_MAX+1]; + if (((!wchar_private(c))) && (c > 31) && (c != 127)) { + if (can_read(0)) { + wchar_t arr[READAHEAD_MAX + 1]; size_t i; - size_t limit = 0 < nchars ? std::min((size_t)nchars - data->command_line.size(), (size_t)READAHEAD_MAX) + size_t limit = 0 < nchars ? std::min((size_t)nchars - data->command_line.size(), + (size_t)READAHEAD_MAX) : READAHEAD_MAX; memset(arr, 0, sizeof(arr)); arr[0] = c; - for (i = 1; i < limit; ++i) - { - - if (!can_read(0)) - { + for (i = 1; i < limit; ++i) { + if (!can_read(0)) { c = 0; break; } - // only allow commands on the first key; otherwise, we might - // have data we need to insert on the commandline that the - // commmand might need to be able to see. + // Only allow commands on the first key; otherwise, we might have data we + // need to insert on the commandline that the commmand might need to be able + // to see. c = input_readch(false); - if ((!wchar_private(c)) && (c>31) && (c != 127)) - { - arr[i]=c; - c=0; - } - else + if ((!wchar_private(c)) && (c > 31) && (c != 127)) { + arr[i] = c; + c = 0; + } else break; } editable_line_t *el = data->active_edit_line(); insert_string(el, arr, true); - /* End paging upon inserting into the normal command line */ - if (el == &data->command_line) - { + // End paging upon inserting into the normal command line. + if (el == &data->command_line) { clear_pager(); } last_char = c; } } - if (c != 0) - break; + if (c != 0) break; - if (0 < nchars && (size_t)nchars <= data->command_line.size()) - { + if (0 < nchars && (size_t)nchars <= data->command_line.size()) { c = R_NULL; break; } } - /* If we get something other than a repaint, then stop coalescing them */ - if (c != R_REPAINT) - coalescing_repaints = false; + // If we get something other than a repaint, then stop coalescing them. + if (c != R_REPAINT) coalescing_repaints = false; - if (last_char != R_YANK && last_char != R_YANK_POP) - yank_len=0; + if (last_char != R_YANK && last_char != R_YANK_POP) yank_len = 0; - /* Restore the text */ - if (c == R_CANCEL && data->is_navigating_pager_contents()) - { - set_command_line_and_position(&data->command_line, data->cycle_command_line, data->cycle_cursor_pos); + // Restore the text. + if (c == R_CANCEL && data->is_navigating_pager_contents()) { + set_command_line_and_position(&data->command_line, data->cycle_command_line, + data->cycle_cursor_pos); } - - /* Clear the pager if necessary */ + // Clear the pager if necessary. bool focused_on_search_field = (data->active_edit_line() == &data->pager.search_field_line); - if (command_ends_paging(c, focused_on_search_field)) - { + if (command_ends_paging(c, focused_on_search_field)) { clear_pager(); } + // fprintf(stderr, "\n\nchar: %ls\n\n", describe_char(c).c_str()); - //fprintf(stderr, "\n\nchar: %ls\n\n", describe_char(c).c_str()); - - switch (c) - { - /* go to beginning of line*/ - case R_BEGINNING_OF_LINE: - { + switch (c) { + // Go to beginning of line. + case R_BEGINNING_OF_LINE: { editable_line_t *el = data->active_edit_line(); - while (el->position > 0 && el->text.at(el->position - 1) != L'\n') - { + while (el->position > 0 && el->text.at(el->position - 1) != L'\n') { update_buff_pos(el, el->position - 1); } reader_repaint_needed(); break; } - - case R_END_OF_LINE: - { + case R_END_OF_LINE: { editable_line_t *el = data->active_edit_line(); - if (el->position < el->size()) - { + if (el->position < el->size()) { const wchar_t *buff = el->text.c_str(); - while (buff[el->position] && - buff[el->position] != L'\n') - { + while (buff[el->position] && buff[el->position] != L'\n') { update_buff_pos(el, el->position + 1); } - } - else - { + } else { accept_autosuggestion(true); } reader_repaint_needed(); break; } - - - case R_BEGINNING_OF_BUFFER: - { + case R_BEGINNING_OF_BUFFER: { update_buff_pos(&data->command_line, 0); reader_repaint_needed(); break; } - - /* go to EOL*/ - case R_END_OF_BUFFER: - { + case R_END_OF_BUFFER: { update_buff_pos(&data->command_line, data->command_line.size()); - reader_repaint_needed(); break; } - - case R_NULL: - { + case R_NULL: { break; } - - case R_CANCEL: - { - // The only thing we can cancel right now is paging, which we handled up above + case R_CANCEL: { + // The only thing we can cancel right now is paging, which we handled up above. break; } - case R_FORCE_REPAINT: - case R_REPAINT: - { - if (! coalescing_repaints) - { + case R_REPAINT: { + if (!coalescing_repaints) { coalescing_repaints = true; exec_prompt(); s_reset(&data->screen, screen_reset_current_line_and_prompt); @@ -3249,205 +2566,178 @@ const wchar_t *reader_readline(int nchars) } break; } - - case R_EOF: - { + case R_EOF: { exit_forced = 1; - data->end_loop=1; + data->end_loop = 1; break; } - - /* complete */ case R_COMPLETE: - case R_COMPLETE_AND_SEARCH: - { + case R_COMPLETE_AND_SEARCH: { + if (!data->complete_func) break; - if (!data->complete_func) - break; - - /* Use the command line only; it doesn't make sense to complete in any other line */ + // Use the command line only; it doesn't make sense to complete in any other line. editable_line_t *el = &data->command_line; - if (data->is_navigating_pager_contents() || (! comp_empty && last_char == R_COMPLETE)) - { - /* The user typed R_COMPLETE more than once in a row. If we are not yet fully disclosed, then become so; otherwise cycle through our available completions. */ - if (data->current_page_rendering.remaining_to_disclose > 0) - { + if (data->is_navigating_pager_contents() || + (!comp_empty && last_char == R_COMPLETE)) { + // The user typed R_COMPLETE more than once in a row. If we are not yet fully + // disclosed, then become so; otherwise cycle through our available completions. + if (data->current_page_rendering.remaining_to_disclose > 0) { data->pager.set_fully_disclosed(true); reader_repaint_needed(); + } else { + select_completion_in_direction(c == R_COMPLETE ? direction_next + : direction_prev); } - else - { - select_completion_in_direction(c == R_COMPLETE ? direction_next : direction_prev); - } - } - else - { - /* Either the user hit tab only once, or we had no visible completion list. */ - - /* Remove a trailing backslash. This may trigger an extra repaint, but this is rare. */ - if (is_backslashed(el->text, el->position)) - { + } else { + // Either the user hit tab only once, or we had no visible completion list. + // Remove a trailing backslash. This may trigger an extra repaint, but this is + // rare. + if (is_backslashed(el->text, el->position)) { remove_backward(); } - /* Get the string; we have to do this after removing any trailing backslash */ - const wchar_t * const buff = el->text.c_str(); + // Get the string; we have to do this after removing any trailing backslash. + const wchar_t *const buff = el->text.c_str(); - /* Clear the completion list */ + // Clear the completion list. comp.clear(); - /* Figure out the extent of the command substitution surrounding the cursor. This is because we only look at the current command substitution to form completions - stuff happening outside of it is not interesting. */ + // Figure out the extent of the command substitution surrounding the cursor. + // This is because we only look at the current command substitution to form + // completions - stuff happening outside of it is not interesting. const wchar_t *cmdsub_begin, *cmdsub_end; parse_util_cmdsubst_extent(buff, el->position, &cmdsub_begin, &cmdsub_end); - /* Figure out the extent of the token within the command substitution. Note we pass cmdsub_begin here, not buff */ + // Figure out the extent of the token within the command substitution. Note we + // pass cmdsub_begin here, not buff. const wchar_t *token_begin, *token_end; - parse_util_token_extent(cmdsub_begin, el->position - (cmdsub_begin-buff), &token_begin, &token_end, 0, 0); + parse_util_token_extent(cmdsub_begin, el->position - (cmdsub_begin - buff), + &token_begin, &token_end, 0, 0); - /* Hack: the token may extend past the end of the command substitution, e.g. in (echo foo) the last token is 'foo)'. Don't let that happen. */ + // Hack: the token may extend past the end of the command substitution, e.g. in + // (echo foo) the last token is 'foo)'. Don't let that happen. if (token_end > cmdsub_end) token_end = cmdsub_end; - /* Figure out how many steps to get from the current position to the end of the current token. */ + // Figure out how many steps to get from the current position to the end of the + // current token. size_t end_of_token_offset = token_end - buff; - /* Move the cursor to the end */ - if (el->position != end_of_token_offset) - { + // Move the cursor to the end. + if (el->position != end_of_token_offset) { update_buff_pos(el, end_of_token_offset); reader_repaint(); } - /* Construct a copy of the string from the beginning of the command substitution up to the end of the token we're completing */ + // Construct a copy of the string from the beginning of the command substitution + // up to the end of the token we're completing. const wcstring buffcpy = wcstring(cmdsub_begin, token_end); - //fprintf(stderr, "Complete (%ls)\n", buffcpy.c_str()); - complete_flags_t complete_flags = COMPLETION_REQUEST_DEFAULT | COMPLETION_REQUEST_DESCRIPTIONS | COMPLETION_REQUEST_FUZZY_MATCH; - data->complete_func(buffcpy, &comp, complete_flags, env_vars_snapshot_t::current()); + // fprintf(stderr, "Complete (%ls)\n", buffcpy.c_str()); + complete_flags_t complete_flags = COMPLETION_REQUEST_DEFAULT | + COMPLETION_REQUEST_DESCRIPTIONS | + COMPLETION_REQUEST_FUZZY_MATCH; + data->complete_func(buffcpy, &comp, complete_flags, + env_vars_snapshot_t::current()); - /* Munge our completions */ + // Munge our completions. completions_sort_and_prioritize(&comp); - /* Record our cycle_command_line */ + // Record our cycle_command_line. data->cycle_command_line = el->text; data->cycle_cursor_pos = el->position; bool continue_after_prefix_insertion = (c == R_COMPLETE_AND_SEARCH); comp_empty = handle_completions(comp, continue_after_prefix_insertion); - /* Show the search field if requested and if we printed a list of completions */ - if (c == R_COMPLETE_AND_SEARCH && ! comp_empty && ! data->pager.empty()) - { + // Show the search field if requested and if we printed a list of completions. + if (c == R_COMPLETE_AND_SEARCH && !comp_empty && !data->pager.empty()) { data->pager.set_search_field_shown(true); select_completion_in_direction(direction_next); reader_repaint_needed(); } - } - break; } - - /* kill */ - case R_KILL_LINE: - { + case R_KILL_LINE: { editable_line_t *el = data->active_edit_line(); const wchar_t *buff = el->text.c_str(); const wchar_t *begin = &buff[el->position]; const wchar_t *end = begin; - while (*end && *end != L'\n') - end++; + while (*end && *end != L'\n') end++; - if (end==begin && *end) - end++; + if (end == begin && *end) end++; - size_t len = end-begin; - if (len) - { - reader_kill(el, begin - buff, len, KILL_APPEND, last_char!=R_KILL_LINE); + size_t len = end - begin; + if (len) { + reader_kill(el, begin - buff, len, KILL_APPEND, last_char != R_KILL_LINE); } - break; } - - case R_BACKWARD_KILL_LINE: - { + case R_BACKWARD_KILL_LINE: { editable_line_t *el = data->active_edit_line(); - if (el->position > 0) - { + if (el->position > 0) { const wchar_t *buff = el->text.c_str(); const wchar_t *end = &buff[el->position]; const wchar_t *begin = end; - /* Make sure we delete at least one character (see #580) */ - begin--; + begin--; // make sure we delete at least one character (see issue #580) - /* Delete until we hit a newline, or the beginning of the string */ - while (begin > buff && *begin != L'\n') - begin--; + // Delete until we hit a newline, or the beginning of the string. + while (begin > buff && *begin != L'\n') begin--; - /* If we landed on a newline, don't delete it */ - if (*begin == L'\n') - begin++; + // If we landed on a newline, don't delete it. + if (*begin == L'\n') begin++; assert(end >= begin); - size_t len = maxi(end-begin, 1); + size_t len = maxi(end - begin, 1); begin = end - len; - reader_kill(el, begin - buff, len, KILL_PREPEND, last_char!=R_BACKWARD_KILL_LINE); + reader_kill(el, begin - buff, len, KILL_PREPEND, + last_char != R_BACKWARD_KILL_LINE); } break; - } - - case R_KILL_WHOLE_LINE: - { - /* We match the emacs behavior here: "kills the entire line including the following newline" */ + case R_KILL_WHOLE_LINE: { + // We match the emacs behavior here: "kills the entire line including the following + // newline". editable_line_t *el = data->active_edit_line(); const wchar_t *buff = el->text.c_str(); - /* Back up to the character just past the previous newline, or go to the beginning of the command line. Note that if the position is on a newline, visually this looks like the cursor is at the end of a line. Therefore that newline is NOT the beginning of a line; this justifies the -1 check. */ + // Back up to the character just past the previous newline, or go to the beginning + // of the command line. Note that if the position is on a newline, visually this + // looks like the cursor is at the end of a line. Therefore that newline is NOT the + // beginning of a line; this justifies the -1 check. size_t begin = el->position; - while (begin > 0 && buff[begin-1] != L'\n') - { + while (begin > 0 && buff[begin - 1] != L'\n') { begin--; } - /* Push end forwards to just past the next newline, or just past the last char. */ + // Push end forwards to just past the next newline, or just past the last char. size_t end = el->position; - while (buff[end] != L'\0') - { + while (buff[end] != L'\0') { end++; - if (buff[end-1] == L'\n') - { + if (buff[end - 1] == L'\n') { break; } } assert(end >= begin); - if (end > begin) - { - reader_kill(el, begin, end - begin, KILL_APPEND, last_char!=R_KILL_WHOLE_LINE); + if (end > begin) { + reader_kill(el, begin, end - begin, KILL_APPEND, + last_char != R_KILL_WHOLE_LINE); } break; } - - /* yank*/ - case R_YANK: - { + case R_YANK: { yank_str = kill_yank(); insert_string(data->active_edit_line(), yank_str); yank_len = wcslen(yank_str); break; } - - /* rotate killring*/ - case R_YANK_POP: - { - if (yank_len) - { - for (size_t i=0; iactive_edit_line(), yank_str); @@ -3455,22 +2745,16 @@ const wchar_t *reader_readline(int nchars) } break; } + // Escape was pressed. + case L'\x1b': { + if (data->search_mode) { + data->search_mode = NO_SEARCH; - /* Escape was pressed */ - case L'\x1b': - { - if (data->search_mode) - { - data->search_mode= NO_SEARCH; - - if (data->token_history_pos==-1) - { - //history_reset(); + if (data->token_history_pos == -1) { + // history_reset(); data->history_search.go_to_end(); reader_set_buffer(data->search_buff, data->search_buff.size()); - } - else - { + } else { reader_replace_current_token(data->search_buff); } data->search_buff.clear(); @@ -3480,399 +2764,298 @@ const wchar_t *reader_readline(int nchars) break; } - - /* delete backward*/ - case R_BACKWARD_DELETE_CHAR: - { + 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. - */ + case R_DELETE_CHAR: { + // Remove the current character in the character buffer and on the screen using + // syntax highlighting, etc. editable_line_t *el = data->active_edit_line(); - if (el->position < el->size()) - { + if (el->position < el->size()) { update_buff_pos(el, el->position + 1); 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 */ + // 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(); - /* If the user hits return while navigating the pager, it only clears the pager */ - if (data->is_navigating_pager_contents()) - { + // If the user hits return while navigating the pager, it only clears the pager. + if (data->is_navigating_pager_contents()) { clear_pager(); break; } - /* The user may have hit return with pager contents, but while not navigating them. Clear the pager in that event. */ + // The user may have hit return with pager contents, but while not navigating them. + // Clear the pager in that event. clear_pager(); - /* We only execute the command line */ + // We only execute the command line. editable_line_t *el = &data->command_line; - /* Allow backslash-escaped newlines, but only if the following character is whitespace, or we're at the end of the text (see issue #613) and not in a comment (#1255). */ - if (is_backslashed(el->text, el->position)) - { + // Allow backslash-escaped newlines, but only if the following character is + // whitespace, or we're at the end of the text (see issue #613) and not in a comment + // (issue #1255). + if (is_backslashed(el->text, el->position)) { bool continue_on_next_line = false; - if (el->position >= el->size()) - { - continue_on_next_line = ! text_ends_in_comment(el->text); - } - else - { + if (el->position >= el->size()) { + continue_on_next_line = !text_ends_in_comment(el->text); + } else { continue_on_next_line = iswspace(el->text.at(el->position)); } - if (continue_on_next_line) - { + if (continue_on_next_line) { insert_char(el, '\n'); break; } } - /* See if this command is valid */ + // See if this command is valid. int command_test_result = data->test_func(el->text.c_str()); - if (command_test_result == 0 || command_test_result == PARSER_TEST_INCOMPLETE) - { - /* This command is valid, but an abbreviation may make it invalid. If so, we will have to test again. */ + if (command_test_result == 0 || command_test_result == PARSER_TEST_INCOMPLETE) { + // This command is valid, but an abbreviation may make it invalid. If so, we + // will have to test again. bool abbreviation_expanded = data->expand_abbreviation_as_necessary(1); - if (abbreviation_expanded) - { - /* It's our reponsibility to rehighlight and repaint. But everything we do below triggers a repaint. */ + if (abbreviation_expanded) { + // It's our reponsibility to rehighlight and repaint. But everything we do + // below triggers a repaint. command_test_result = data->test_func(el->text.c_str()); - /* If the command is OK, then we're going to execute it. We still want to do syntax highlighting, but a synchronous variant that performs no I/O, so as not to block the user */ + // If the command is OK, then we're going to execute it. We still want to do + // syntax highlighting, but a synchronous variant that performs no I/O, so + // as not to block the user. bool skip_io = (command_test_result == 0); reader_super_highlight_me_plenty(0, skip_io); } } - switch (command_test_result) - { - - case 0: - { - /* Finished command, execute it. Don't add items that start with a leading space. */ + switch (command_test_result) { + case 0: { + // Finished command, execute it. Don't add items that start with a leading + // space. const editable_line_t *el = &data->command_line; - if (data->history != NULL && ! el->empty() && el->text.at(0) != L' ') - { + if (data->history != NULL && !el->empty() && el->text.at(0) != L' ') { data->history->add_pending_with_file_detection(el->text); } - finished=1; + finished = 1; update_buff_pos(&data->command_line, data->command_line.size()); reader_repaint(); break; } - - /* - We are incomplete, continue editing - */ - case PARSER_TEST_INCOMPLETE: - { + case PARSER_TEST_INCOMPLETE: { + // We are incomplete, continue editing. insert_char(el, '\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: - { + default: { + // Result must be some combination including an error. The error message + // will already be printed, all we need to do is repaint. s_reset(&data->screen, screen_reset_abandon_line); reader_repaint_needed(); 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: - { + case R_HISTORY_TOKEN_SEARCH_FORWARD: { int reset = 0; - if (data->search_mode == NO_SEARCH) - { + if (data->search_mode == NO_SEARCH) { reset = 1; - if ((c == R_HISTORY_SEARCH_BACKWARD) || - (c == R_HISTORY_SEARCH_FORWARD)) - { + if ((c == R_HISTORY_SEARCH_BACKWARD) || (c == R_HISTORY_SEARCH_FORWARD)) { data->search_mode = LINE_SEARCH; - } - else - { + } else { data->search_mode = TOKEN_SEARCH; } const editable_line_t *el = &data->command_line; data->search_buff.append(el->text); - data->history_search = history_search_t(*data->history, data->search_buff, HISTORY_SEARCH_TYPE_CONTAINS); + data->history_search = history_search_t(*data->history, data->search_buff, + HISTORY_SEARCH_TYPE_CONTAINS); - /* Skip the autosuggestion as history unless it was truncated */ + // Skip the autosuggestion as history unless it was truncated. const wcstring &suggest = data->autosuggestion; - if (! suggest.empty() && ! data->screen.autosuggestion_is_truncated) - { + if (!suggest.empty() && !data->screen.autosuggestion_is_truncated) { data->history_search.skip_matches(wcstring_list_t(&suggest, 1 + &suggest)); } } - switch (data->search_mode) - { - case LINE_SEARCH: - { + switch (data->search_mode) { + case LINE_SEARCH: { if ((c == R_HISTORY_SEARCH_BACKWARD) || - (c == R_HISTORY_TOKEN_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 */ + } 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()) - { + if (data->history_search.is_at_end()) { new_text = data->search_buff; - } - else - { + } else { new_text = data->history_search.current_string(); } - set_command_line_and_position(&data->command_line, new_text, new_text.size()); - + set_command_line_and_position(&data->command_line, new_text, + new_text.size()); break; } - - case TOKEN_SEARCH: - { + case TOKEN_SEARCH: { if ((c == R_HISTORY_SEARCH_BACKWARD) || - (c == R_HISTORY_TOKEN_SEARCH_BACKWARD)) - { + (c == R_HISTORY_TOKEN_SEARCH_BACKWARD)) { handle_token_history(SEARCH_BACKWARD, reset); - } - else - { + } else { handle_token_history(SEARCH_FORWARD, reset); } - break; } - } break; } - - - /* Move left*/ - case R_BACKWARD_CHAR: - { + case R_BACKWARD_CHAR: { editable_line_t *el = data->active_edit_line(); - if (data->is_navigating_pager_contents()) - { + if (data->is_navigating_pager_contents()) { select_completion_in_direction(direction_west); - } - else if (el->position > 0) - { - update_buff_pos(el, el->position-1); + } else if (el->position > 0) { + update_buff_pos(el, el->position - 1); reader_repaint_needed(); } break; } - - /* Move right*/ - case R_FORWARD_CHAR: - { + case R_FORWARD_CHAR: { editable_line_t *el = data->active_edit_line(); - if (data->is_navigating_pager_contents()) - { + if (data->is_navigating_pager_contents()) { select_completion_in_direction(direction_east); - } - else if (el->position < el->size()) - { + } else if (el->position < el->size()) { update_buff_pos(el, el->position + 1); reader_repaint_needed(); - } - else - { + } else { accept_autosuggestion(true); } break; } - - /* kill one word left */ case R_BACKWARD_KILL_WORD: case R_BACKWARD_KILL_PATH_COMPONENT: - case R_BACKWARD_KILL_BIGWORD: - { + case R_BACKWARD_KILL_BIGWORD: { move_word_style_t style = - (c == R_BACKWARD_KILL_BIGWORD ? move_word_style_whitespace : - c == R_BACKWARD_KILL_PATH_COMPONENT ? move_word_style_path_components : move_word_style_punctuation); - bool newv = (last_char != R_BACKWARD_KILL_WORD && last_char != R_BACKWARD_KILL_PATH_COMPONENT && last_char != R_BACKWARD_KILL_BIGWORD); + (c == R_BACKWARD_KILL_BIGWORD + ? move_word_style_whitespace + : c == R_BACKWARD_KILL_PATH_COMPONENT ? move_word_style_path_components + : move_word_style_punctuation); + bool newv = (last_char != R_BACKWARD_KILL_WORD && + last_char != R_BACKWARD_KILL_PATH_COMPONENT && + last_char != R_BACKWARD_KILL_BIGWORD); move_word(data->active_edit_line(), MOVE_DIR_LEFT, true /* erase */, style, newv); break; } - - /* kill one word right */ - case R_KILL_WORD: - { - move_word(data->active_edit_line(), MOVE_DIR_RIGHT, true /* erase */, move_word_style_punctuation, last_char!=R_KILL_WORD); + case R_KILL_WORD: { + move_word(data->active_edit_line(), MOVE_DIR_RIGHT, true /* erase */, + move_word_style_punctuation, last_char != R_KILL_WORD); break; } - - /* kill one bigword right */ - case R_KILL_BIGWORD: - { - move_word(data->active_edit_line(), MOVE_DIR_RIGHT, true /* erase */, move_word_style_whitespace, last_char!=R_KILL_BIGWORD); + case R_KILL_BIGWORD: { + move_word(data->active_edit_line(), MOVE_DIR_RIGHT, true /* erase */, + move_word_style_whitespace, last_char != R_KILL_BIGWORD); break; } - - /* move one word left*/ - case R_BACKWARD_WORD: - { - move_word(data->active_edit_line(), MOVE_DIR_LEFT, false /* do not erase */, move_word_style_punctuation, false); + case R_BACKWARD_WORD: { + move_word(data->active_edit_line(), MOVE_DIR_LEFT, false /* do not erase */, + move_word_style_punctuation, false); break; } - - /* move one bigword left */ - case R_BACKWARD_BIGWORD: - { - move_word(data->active_edit_line(), MOVE_DIR_LEFT, false /* do not erase */, move_word_style_whitespace, false); + case R_BACKWARD_BIGWORD: { + move_word(data->active_edit_line(), MOVE_DIR_LEFT, false /* do not erase */, + move_word_style_whitespace, false); break; } - - /* move one word right*/ - case R_FORWARD_WORD: - { + case R_FORWARD_WORD: { editable_line_t *el = data->active_edit_line(); - if (el->position < el->size()) - { - move_word(el, MOVE_DIR_RIGHT, false /* do not erase */, move_word_style_punctuation, false); - } - else - { + if (el->position < el->size()) { + move_word(el, MOVE_DIR_RIGHT, false /* do not erase */, + move_word_style_punctuation, false); + } else { accept_autosuggestion(false /* accept only one word */); } break; } - - /* move one bigword right */ - case R_FORWARD_BIGWORD: - { + case R_FORWARD_BIGWORD: { editable_line_t *el = data->active_edit_line(); - if (el->position < el->size()) - { - move_word(el, MOVE_DIR_RIGHT, false /* do not erase */, move_word_style_whitespace, false); - } - else - { + if (el->position < el->size()) { + move_word(el, MOVE_DIR_RIGHT, false /* do not erase */, + move_word_style_whitespace, false); + } else { accept_autosuggestion(false /* accept only one word */); } break; } - - case R_BEGINNING_OF_HISTORY: - { - if (data->is_navigating_pager_contents()) - { - select_completion_in_direction(direction_page_north); + case R_BEGINNING_OF_HISTORY: { + if (data->is_navigating_pager_contents()) { + select_completion_in_direction(direction_page_north); } else { const editable_line_t *el = &data->command_line; - data->history_search = history_search_t(*data->history, el->text, HISTORY_SEARCH_TYPE_PREFIX); + data->history_search = + history_search_t(*data->history, el->text, HISTORY_SEARCH_TYPE_PREFIX); data->history_search.go_to_beginning(); - if (! data->history_search.is_at_end()) - { + if (!data->history_search.is_at_end()) { wcstring new_text = data->history_search.current_string(); - set_command_line_and_position(&data->command_line, new_text, new_text.size()); + set_command_line_and_position(&data->command_line, new_text, + new_text.size()); } } break; } - - case R_END_OF_HISTORY: - { - if (data->is_navigating_pager_contents()) - { + case R_END_OF_HISTORY: { + if (data->is_navigating_pager_contents()) { select_completion_in_direction(direction_page_south); } else { data->history_search.go_to_end(); } break; } - case R_UP_LINE: - case R_DOWN_LINE: - { - if (data->is_navigating_pager_contents()) - { - /* We are already navigating pager contents. */ + case R_DOWN_LINE: { + if (data->is_navigating_pager_contents()) { + // We are already navigating pager contents. selection_direction_t direction; - if (c == R_DOWN_LINE) - { - /* Down arrow is always south */ + if (c == R_DOWN_LINE) { + // Down arrow is always south. direction = direction_south; - } - else if (selection_is_at_top()) - { - /* Up arrow, but we are in the first column and first row. End navigation */ + } else if (selection_is_at_top()) { + // Up arrow, but we are in the first column and first row. End navigation. direction = direction_deselect; - } - else - { - /* Up arrow, go north */ + } else { + // Up arrow, go north. direction = direction_north; } - /* Now do the selection */ + // Now do the selection. select_completion_in_direction(direction); - } - else if (c == R_DOWN_LINE && ! data->pager.empty()) - { - /* We pressed down with a non-empty pager contents, begin navigation */ + } else if (c == R_DOWN_LINE && !data->pager.empty()) { + // We pressed down with a non-empty pager contents, begin navigation. select_completion_in_direction(direction_south); - } - else - { - /* Not navigating the pager contents */ + } else { + // Not navigating the pager contents. editable_line_t *el = data->active_edit_line(); int line_old = parse_util_get_line_from_offset(el->text, el->position); int line_new; if (c == R_UP_LINE) - line_new = line_old-1; + line_new = line_old - 1; else - line_new = line_old+1; + line_new = line_old + 1; - int line_count = parse_util_lineno(el->text.c_str(), el->size())-1; + int line_count = parse_util_lineno(el->text.c_str(), el->size()) - 1; - if (line_new >= 0 && line_new <= line_count) - { + if (line_new >= 0 && line_new <= line_count) { size_t base_pos_new; size_t base_pos_old; @@ -3883,130 +3066,115 @@ const wchar_t *reader_readline(int nchars) base_pos_new = parse_util_get_offset_from_line(el->text, line_new); - base_pos_old = parse_util_get_offset_from_line(el->text, line_old); + base_pos_old = parse_util_get_offset_from_line(el->text, 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 = el->position - parse_util_get_offset_from_line(el->text, line_old); - total_offset_new = parse_util_get_offset(el->text, line_new, line_offset_old - 4*(indent_new-indent_old)); + line_offset_old = + el->position - parse_util_get_offset_from_line(el->text, line_old); + total_offset_new = parse_util_get_offset( + el->text, line_new, line_offset_old - 4 * (indent_new - indent_old)); update_buff_pos(el, total_offset_new); reader_repaint_needed(); } } - break; } - - case R_SUPPRESS_AUTOSUGGESTION: - { + case R_SUPPRESS_AUTOSUGGESTION: { data->suppress_autosuggestion = true; data->autosuggestion.clear(); reader_repaint_needed(); break; } - - case R_ACCEPT_AUTOSUGGESTION: - { + case R_ACCEPT_AUTOSUGGESTION: { accept_autosuggestion(true); break; } - - case R_TRANSPOSE_CHARS: - { + case R_TRANSPOSE_CHARS: { editable_line_t *el = data->active_edit_line(); - if (el->size() < 2) - { + if (el->size() < 2) { break; } - /* If the cursor is at the end, transpose the last two characters of the line */ - if (el->position == el->size()) - { + // If the cursor is at the end, transpose the last two characters of the line. + if (el->position == el->size()) { update_buff_pos(el, el->position - 1); } - /* - Drag the character before the cursor forward over the character at the cursor, moving - the cursor forward as well. - */ - if (el->position > 0) - { + // Drag the character before the cursor forward over the character at the cursor, + // moving the cursor forward as well. + if (el->position > 0) { wcstring local_cmd = el->text; - std::swap(local_cmd.at(el->position), local_cmd.at(el->position-1)); + std::swap(local_cmd.at(el->position), local_cmd.at(el->position - 1)); set_command_line_and_position(el, local_cmd, el->position + 1); } break; } - - case R_TRANSPOSE_WORDS: - { + case R_TRANSPOSE_WORDS: { editable_line_t *el = data->active_edit_line(); size_t len = el->size(); const wchar_t *buff = el->text.c_str(); const wchar_t *tok_begin, *tok_end, *prev_begin, *prev_end; - /* If we are not in a token, look for one ahead */ + // If we are not in a token, look for one ahead. size_t buff_pos = el->position; - while (buff_pos != len && !iswalnum(buff[buff_pos])) - buff_pos++; + while (buff_pos != len && !iswalnum(buff[buff_pos])) buff_pos++; update_buff_pos(el, buff_pos); - parse_util_token_extent(buff, el->position, &tok_begin, &tok_end, &prev_begin, &prev_end); + parse_util_token_extent(buff, el->position, &tok_begin, &tok_end, &prev_begin, + &prev_end); - /* In case we didn't find a token at or after the cursor... */ - if (tok_begin == &buff[len]) - { - /* ...retry beginning from the previous token */ + // In case we didn't find a token at or after the cursor... + if (tok_begin == &buff[len]) { + // ...retry beginning from the previous token. size_t pos = prev_end - &buff[0]; - parse_util_token_extent(buff, pos, &tok_begin, &tok_end, &prev_begin, &prev_end); + parse_util_token_extent(buff, pos, &tok_begin, &tok_end, &prev_begin, + &prev_end); } - /* Make sure we have two tokens */ - if (prev_begin < prev_end && tok_begin < tok_end && tok_begin > prev_begin) - { + // Make sure we have two tokens. + if (prev_begin < prev_end && tok_begin < tok_end && tok_begin > prev_begin) { const wcstring prev(prev_begin, prev_end - prev_begin); const wcstring sep(prev_end, tok_begin - prev_end); const wcstring tok(tok_begin, tok_end - tok_begin); const wcstring trail(tok_end, &buff[len] - tok_end); - /* Compose new command line with swapped tokens */ + // Compose new command line with swapped tokens. wcstring new_buff(buff, prev_begin - buff); new_buff.append(tok); new_buff.append(sep); new_buff.append(prev); new_buff.append(trail); - /* Put cursor right after the second token */ + // Put cursor right after the second token. set_command_line_and_position(el, new_buff, tok_end - buff); } break; } - case R_UPCASE_WORD: case R_DOWNCASE_WORD: - case R_CAPITALIZE_WORD: - { + case R_CAPITALIZE_WORD: { editable_line_t *el = data->active_edit_line(); - // For capitalize_word, whether we've capitalized a character so far + // For capitalize_word, whether we've capitalized a character so far. bool capitalized_first = false; - // We apply the operation from the current location to the end of the word + // We apply the operation from the current location to the end of the word. size_t pos = el->position; move_word(el, MOVE_DIR_RIGHT, false, move_word_style_punctuation, false); - for (; pos < el->position; pos++) - { + for (; pos < el->position; pos++) { wchar_t chr = el->text.at(pos); - // We always change the case; this decides whether we go uppercase (true) or lowercase (false) + // We always change the case; this decides whether we go uppercase (true) or + // lowercase (false). bool make_uppercase; if (c == R_CAPITALIZE_WORD) - make_uppercase = ! capitalized_first && iswalnum(chr); + make_uppercase = !capitalized_first && iswalnum(chr); else make_uppercase = (c == R_UPCASE_WORD); - // Apply the operation and then record what we did + // Apply the operation and then record what we did. if (make_uppercase) chr = towupper(chr); else @@ -4020,18 +3188,14 @@ const wchar_t *reader_readline(int nchars) reader_repaint_needed(); break; } - - case R_BEGIN_SELECTION: - { + case R_BEGIN_SELECTION: { data->sel_active = true; data->sel_begin_pos = data->command_line.position; data->sel_start_pos = data->command_line.position; data->sel_stop_pos = data->command_line.position; break; } - - case R_SWAP_SELECTION_START_STOP: - { + case R_SWAP_SELECTION_START_STOP: { if (!data->sel_active) break; size_t tmp = data->sel_begin_pos; data->sel_begin_pos = data->command_line.position; @@ -4040,36 +3204,27 @@ const wchar_t *reader_readline(int nchars) update_buff_pos(el, tmp); break; } - - case R_END_SELECTION: - { + case R_END_SELECTION: { data->sel_active = false; data->sel_start_pos = data->command_line.position; data->sel_stop_pos = data->command_line.position; break; } - - case R_KILL_SELECTION: - { + case R_KILL_SELECTION: { bool newv = (last_char != R_KILL_SELECTION); size_t start, len; - if (reader_get_selection(&start, &len)) - { + if (reader_get_selection(&start, &len)) { reader_kill(&data->command_line, start, len, KILL_APPEND, newv); } break; } - - case R_FORWARD_JUMP: - { + case R_FORWARD_JUMP: { editable_line_t *el = data->active_edit_line(); wchar_t target = input_function_pop_arg(); bool status = false; - for (size_t i = el->position + 1; i < el->size(); i++) - { - if (el->at(i) == target) - { + for (size_t i = el->position + 1; i < el->size(); i++) { + if (el->at(i) == target) { update_buff_pos(el, i); status = true; break; @@ -4079,18 +3234,14 @@ const wchar_t *reader_readline(int nchars) reader_repaint_needed(); break; } - - case R_BACKWARD_JUMP: - { + case R_BACKWARD_JUMP: { editable_line_t *el = data->active_edit_line(); wchar_t target = input_function_pop_arg(); bool status = false; size_t tmp_pos = el->position; - while (tmp_pos--) - { - if (el->at(tmp_pos) == target) - { + while (tmp_pos--) { + if (el->at(tmp_pos) == target) { update_buff_pos(el, tmp_pos); status = true; break; @@ -4100,60 +3251,41 @@ const wchar_t *reader_readline(int nchars) reader_repaint_needed(); break; } - - /* Other, if a normal character, we add it to the command */ - default: - { - if ((!wchar_private(c)) && (((c>31) || (c==L'\n'))&& (c != 127))) - { + default: { + // Other, if a normal character, we add it to the command. + if ((!wchar_private(c)) && (((c > 31) || (c == L'\n')) && (c != 127))) { bool allow_expand_abbreviations = false; - if (data->is_navigating_pager_contents()) - { + if (data->is_navigating_pager_contents()) { data->pager.set_search_field_shown(true); - } - else - { + } else { allow_expand_abbreviations = true; } - /* Regular character */ + // Regular character. editable_line_t *el = data->active_edit_line(); insert_char(data->active_edit_line(), c, allow_expand_abbreviations); - /* End paging upon inserting into the normal command line */ - if (el == &data->command_line) - { + // End paging upon inserting into the normal command line. + if (el == &data->command_line) { clear_pager(); } - - } - else - { - /* - Low priority debug message. These can happen if - the user presses an unefined control - sequnece. No reason to report. - */ + } 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) && - (c != R_REPAINT) && - (c != R_FORCE_REPAINT)) - { + 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) && (c != R_REPAINT) && (c != R_FORCE_REPAINT)) { data->search_mode = NO_SEARCH; data->search_buff.clear(); data->history_search.go_to_end(); - data->token_history_pos=-1; + data->token_history_pos = -1; } last_char = c; @@ -4163,94 +3295,74 @@ const wchar_t *reader_readline(int nchars) writestr(L"\n"); - /* Ensure we have no pager contents when we exit */ - if (! data->pager.empty()) - { - /* Clear to end of screen to erase the pager contents. TODO: this may fail if eos doesn't exist, in which case we should emit newlines */ + // Ensure we have no pager contents when we exit. + if (!data->pager.empty()) { + // Clear to end of screen to erase the pager contents. + // TODO: this may fail if eos doesn't exist, in which case we should emit newlines. screen_force_clear_to_end(); data->pager.clear(); } - if (!reader_exit_forced()) - { - if (tcsetattr(0,TCSANOW,&old_modes)) /* return to previous mode */ - { - wperror(L"tcsetattr"); + if (!reader_exit_forced()) { + if (tcsetattr(0, TCSANOW, &old_modes)) { + wperror(L"tcsetattr"); // return to previous mode } - set_color(rgb_color_t::reset(), rgb_color_t::reset()); } return finished ? data->command_line.text.c_str() : NULL; } -int reader_search_mode() -{ - if (!data) - { +int reader_search_mode() { + if (!data) { return -1; } - return !!data->search_mode; } -int reader_has_pager_contents() -{ - if (!data) - { +int reader_has_pager_contents() { + if (!data) { return -1; } - return ! data->current_page_rendering.screen_data.empty(); + return !data->current_page_rendering.screen_data.empty(); } -/** - Read non-interactively. Read input from stdin without displaying - 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) -{ +/// Read non-interactively. Read input from stdin without displaying 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) { parser_t &parser = parser_t::principal_parser(); FILE *in_stream; - wchar_t *buff=0; + wchar_t *buff = 0; std::vector acc; int des = (fd == STDIN_FILENO ? dup(STDIN_FILENO) : fd); - int res=0; + int res = 0; - if (des == -1) - { + if (des == -1) { wperror(L"dup"); return 1; } in_stream = fdopen(des, "r"); - if (in_stream != 0) - { - while (!feof(in_stream)) - { + if (in_stream != 0) { + while (!feof(in_stream)) { char buff[4096]; size_t c = fread(buff, 1, 4096, in_stream); - if (ferror(in_stream)) - { - if (errno == EINTR) - { - /* We got a signal, just keep going. Be sure that we call insert() below because we may get data as well as EINTR. */ + if (ferror(in_stream)) { + if (errno == EINTR) { + // We got a signal, just keep going. Be sure that we call insert() below because + // we may get data as well as EINTR. clearerr(in_stream); - } - else if ((errno == EAGAIN || errno == EWOULDBLOCK) && make_fd_blocking(des) == 0) - { - /* We succeeded in making the fd blocking, keep going */ + } else if ((errno == EAGAIN || errno == EWOULDBLOCK) && + make_fd_blocking(des) == 0) { + // We succeeded in making the fd blocking, keep going. clearerr(in_stream); - } - else - { - /* Fatal error */ + } else { + // Fatal error. debug(1, _(L"Error while reading from file descriptor")); - - /* Reset buffer on error. We won't evaluate incomplete files. */ + // Reset buffer on error. We won't evaluate incomplete files. acc.clear(); break; } @@ -4262,66 +3374,49 @@ static int read_ni(int fd, const io_chain_t &io) wcstring str = acc.empty() ? wcstring() : str2wcstring(&acc.at(0), acc.size()); acc.clear(); - if (fclose(in_stream)) - { - debug(1, - _(L"Error while closing input stream")); + if (fclose(in_stream)) { + debug(1, _(L"Error while closing input stream")); wperror(L"fclose"); res = 1; } - /* Swallow a BOM (#1518) */ - if (! str.empty() && str.at(0) == UTF8_BOM_WCHAR) - { + // Swallow a BOM (issue #1518). + if (!str.empty() && str.at(0) == UTF8_BOM_WCHAR) { str.erase(0, 1); } parse_error_list_t errors; parse_node_tree_t tree; - if (! parse_util_detect_errors(str, &errors, false /* do not accept incomplete */, &tree)) - { + if (!parse_util_detect_errors(str, &errors, false /* do not accept incomplete */, &tree)) { parser.eval_acquiring_tree(str, io, TOP, moved_ref(tree)); - } - else - { + } else { wcstring sb; parser.get_backtrace(str, errors, &sb); fwprintf(stderr, L"%ls", sb.c_str()); res = 1; } - } - else - { - debug(1, - _(L"Error while opening input stream")); + } else { + debug(1, _(L"Error while opening input stream")); wperror(L"fdopen"); free(buff); - res=1; + 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; - /* - 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); + 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; + // 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(); diff --git a/src/reader.h b/src/reader.h index 874b4711e..4c5a511ea 100644 --- a/src/reader.h +++ b/src/reader.h @@ -1,18 +1,13 @@ -/** \file reader.h - - Prototypes for functions for reading data from stdin and passing - to the parser. If stdin is a keyboard, it supplies a killring, - history, syntax highlighting, tab-completion and various other - features. -*/ - +// Prototypes for functions for reading data from stdin and passing to the parser. If stdin is a +// keyboard, it supplies a killring, history, syntax highlighting, tab-completion and various other +// features. #ifndef FISH_READER_H #define FISH_READER_H -#include -#include -#include #include +#include +#include +#include #include "common.h" #include "complete.h" @@ -23,299 +18,213 @@ class history_t; class env_vars_snapshot_t; class io_chain_t; -/* Helper class for storing a command line */ -class editable_line_t -{ -public: - - /** The command line */ +/// Helper class for storing a command line. +class editable_line_t { + public: + /// The command line. wcstring text; - - /** The current position of the cursor in the command line */ + /// The current position of the cursor in the command line. size_t position; - const wcstring &get_text() const - { - return text; - } + const wcstring &get_text() const { return text; } - /* Gets the length of the text */ - size_t size() const - { - return text.size(); - } + // Gets the length of the text. + size_t size() const { return text.size(); } - bool empty() const - { - return text.empty(); - } + bool empty() const { return text.empty(); } - void clear() - { + void clear() { text.clear(); position = 0; } - wchar_t at(size_t idx) - { - return text.at(idx); - } + wchar_t at(size_t idx) { return text.at(idx); } - editable_line_t() : text(), position(0) - { - } + editable_line_t() : text(), position(0) {} - /* Inserts a substring of str given by start, len at the cursor position */ + /// Inserts a substring of str given by start, len at the cursor position. void insert_string(const wcstring &str, size_t start = 0, size_t len = wcstring::npos); }; -/** - Read commands from \c fd until encountering EOF -*/ +/// Read commands from \c fd until encountering EOF. int reader_read(int fd, const io_chain_t &io); -/** - Tell the shell that it should exit after the currently running command finishes. -*/ +/// Tell the shell that it should exit after the currently running command finishes. void reader_exit(int do_exit, int force); -/** - Check that the reader is in a sane state -*/ +/// Check that the reader is in a sane state. void reader_sanity_check(); -/** - Initialize the reader -*/ +/// Initialize the reader. void reader_init(); -/** - Destroy and free resources used by the reader -*/ +/// Destroy and free resources used by the reader. void reader_destroy(); -/** Restore the term mode at startup */ +/// Restore the term mode at startup. void restore_term_mode(); -/** - Returns the filename of the file currently read -*/ +/// Returns the filename of the file currently read. const wchar_t *reader_current_filename(); -/** - Push a new filename on the stack of read files - - \param fn The fileanme to push -*/ +/// 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); -/** - Pop the current filename from the stack of read files - */ + +/// Pop the current filename from the stack of read files. void reader_pop_current_filename(); -/** - Write the title to the titlebar. This function is called just - before a new application starts executing and just after it - finishes. - - \param cmd Command line string passed to \c fish_title if is defined. - \param reset_cursor_position If set, issue a \r so the line driver knows where we are -*/ +/// Write the title to the titlebar. This function is called just before a new application starts +/// executing and just after it finishes. +/// +/// \param cmd Command line string passed to \c fish_title if is defined. +/// \param reset_cursor_position If set, issue a \r so the line driver knows where we are void reader_write_title(const wcstring &cmd, bool reset_cursor_position = true); -/** - Call this function to tell the reader that a repaint is needed, and - should be performed when possible. - */ +/// Call this function to tell the reader that a repaint is needed, and should be performed when +/// possible. void reader_repaint_needed(); -/** Call this function to tell the reader that some color has changed. */ +/// Call this function to tell the reader that some color has changed. void reader_react_to_color_change(); -/* Repaint immediately if needed. */ +/// Repaint immediately if needed. 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. -*/ +/// 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 wcstring &buff); -/** - Get the string of character currently entered into the command - buffer, or 0 if interactive mode is uninitialized. -*/ +/// Get the string of character currently entered into the command buffer, or 0 if interactive mode +/// is uninitialized. const wchar_t *reader_get_buffer(); -/** Returns the current reader's history */ +/// Returns the current reader's history. history_t *reader_get_history(void); -/** - Set the string of characters in the command buffer, as well as the cursor position. - - \param b the new buffer value - \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. -*/ +/// Set the string of characters in the command buffer, as well as the cursor position. +/// +/// \param b the new buffer value +/// \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); -/** - Get the current cursor position in the command line. If interactive - mode is uninitialized, return (size_t)(-1). -*/ +/// Get the current cursor position in the command line. If interactive mode is uninitialized, +/// return (size_t)(-1). size_t reader_get_cursor_pos(); - -/** - Get the current selection range in the command line. - Returns false if there is no active selection, true otherwise. -*/ +/// Get the current selection range in the command line. Returns false if there is no active +/// selection, true otherwise. bool reader_get_selection(size_t *start, size_t *len); -/** - Return the value of the interrupted flag, which is set by the sigint - handler, and clear it if it was set. -*/ +/// Return the value of the interrupted flag, which is set by the sigint handler, and clear it if it +/// was set. int reader_interrupted(); -/** - Clear the interrupted flag unconditionally without handling anything. The - flag could have been set e.g. when an interrupt arrived just as we were - ending an earlier \c reader_readline invocation but before the - \c is_interactive_read flag was cleared. -*/ +/// Clear the interrupted flag unconditionally without handling anything. The flag could have been +/// set e.g. when an interrupt arrived just as we were ending an earlier \c reader_readline +/// invocation but before the \c is_interactive_read flag was cleared. void reader_reset_interrupted(); -/** - Return the value of the interrupted flag, which is set by the sigint - handler, and clear it if it was set. If the current reader is interruptible, - call \c reader_exit(). -*/ +/// Return the value of the interrupted flag, which is set by the sigint handler, and clear it if it +/// was set. If the current reader is interruptible, call \c reader_exit(). int reader_reading_interrupted(); -/** - Returns true if the current reader generation count does not equal the - generation count the current thread was started with. - Note 1: currently only valid for autocompletion threads! Other threads don't - set the threadlocal generation count when they start up. -*/ +/// Returns true if the current reader generation count does not equal the generation count the +/// current thread was started with. Note 1: currently only valid for autocompletion threads! Other +/// threads don't set the threadlocal generation count when they start up. bool reader_thread_job_is_stale(); -/** - Read one line of input. Before calling this function, reader_push() must have - been called in order to set up a valid reader environment. If nchars > 0, - return after reading that many characters even if a full line has not yet - been read. Note: the returned value may be longer than nchars if a single - keypress resulted in multiple characters being inserted into the commandline. -*/ +/// Read one line of input. Before calling this function, reader_push() must have been called in +/// order to set up a valid reader environment. If nchars > 0, return after reading that many +/// characters even if a full line has not yet been read. Note: the returned value may be longer +/// than nchars if a single keypress resulted in multiple characters being inserted into the +/// commandline. const wchar_t *reader_readline(int nchars); -/** - Push a new reader environment. -*/ +/// Push a new reader environment. void reader_push(const wchar_t *name); -/** - Return to previous reader environment -*/ +/// Return to previous reader environment. void reader_pop(); -/** - 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 *, completion_request_flags_t, const env_vars_snapshot_t &); +/// 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 *, + completion_request_flags_t, const env_vars_snapshot_t &); void reader_set_complete_function(complete_function_t); -/** - The type of a highlight function. - */ -typedef void (*highlight_function_t)(const wcstring &, std::vector &, size_t, wcstring_list_t *, const env_vars_snapshot_t &vars); +/// The type of a highlight function. +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 - */ +/// 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); -/** - Specify function for testing if the command buffer contains syntax - errors that must be corrected before returning. -*/ +/// Specify function for testing if the command buffer contains syntax errors that must be corrected +/// before returning. void reader_set_test_function(parser_test_error_bits_t (*f)(const wchar_t *)); -/** - Specify string of shell commands to be run in order to generate the - prompt. -*/ +/// Specify string of shell commands to be run in order to generate the prompt. void reader_set_left_prompt(const wcstring &prompt); -/** - Specify string of shell commands to be run in order to generate the - right prompt. -*/ +/// Specify string of shell commands to be run in order to generate the right prompt. void reader_set_right_prompt(const wcstring &prompt); - -/** Sets whether autosuggesting is allowed. */ +/// Sets whether autosuggesting is allowed. void reader_set_allow_autosuggesting(bool flag); -/** Sets whether abbreviation expansion is performed. */ +/// Sets whether abbreviation expansion is performed. void reader_set_expand_abbreviations(bool flag); - -/** Sets whether the reader should exit on ^C. */ +/// Sets whether the reader should exit on ^C. void reader_set_exit_on_interrupt(bool flag); -/** - Returns true if the shell is exiting, 0 otherwise. -*/ +/// Returns true if the shell is exiting, 0 otherwise. bool shell_is_exiting(); -/** - The readers interrupt signal handler. Cancels all currently running blocks. -*/ +/// The readers interrupt signal handler. Cancels all currently running blocks. void reader_handle_int(int signal); -/** - This function returns true if fish is exiting by force, i.e. because stdin died -*/ +/// This function returns true if fish is exiting by force, i.e. because stdin died. int reader_exit_forced(); -/** - Test if the given shell command contains errors. Uses parser_test - for testing. Suitable for reader_set_test_function(). -*/ +/// Test if the given shell command contains errors. Uses parser_test for testing. Suitable for +/// reader_set_test_function(). parser_test_error_bits_t reader_shell_test(const wchar_t *b); -/** - Test whether the interactive reader is in search mode. - - \return 0 if not in search mode, 1 if in search mode and -1 if not in interactive mode - */ +/// Test whether the interactive reader is in search mode. +/// +/// \return 0 if not in search mode, 1 if in search mode and -1 if not in interactive mode int reader_search_mode(); -/** - Test whether the interactive reader has visible pager contents. - - \return 0 if it has pager contents, 1 if it does not have pager contents, and -1 if not in interactive mode - */ +/// Test whether the interactive reader has visible pager contents. +/// +/// \return 0 if it has pager contents, 1 if it does not have pager contents, and -1 if not in +/// interactive mode int reader_has_pager_contents(); +/// Given a command line and an autosuggestion, return the string that gets shown to the user. +/// Exposed for testing purposes only. +wcstring combine_command_and_autosuggestion(const wcstring &cmdline, + const wcstring &autosuggestion); -/* Given a command line and an autosuggestion, return the string that gets shown to the user. Exposed for testing purposes only. */ -wcstring combine_command_and_autosuggestion(const wcstring &cmdline, const wcstring &autosuggestion); +/// Expand abbreviations at the given cursor position. Exposed for testing purposes only. +bool reader_expand_abbreviation_in_command(const wcstring &cmdline, size_t cursor_pos, + wcstring *output); -/* Expand abbreviations at the given cursor position. Exposed for testing purposes only. */ -bool reader_expand_abbreviation_in_command(const wcstring &cmdline, size_t cursor_pos, wcstring *output); - -/* Apply a completion string. Exposed for testing only. */ -wcstring completion_apply_to_command_line(const wcstring &val_str, complete_flags_t flags, const wcstring &command_line, size_t *inout_cursor_pos, bool append_only); +/// Apply a completion string. Exposed for testing only. +wcstring completion_apply_to_command_line(const wcstring &val_str, complete_flags_t flags, + const wcstring &command_line, size_t *inout_cursor_pos, + bool append_only); #endif From fa5356373377a5792e260468b04c8344f0e3448e Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Tue, 3 May 2016 12:31:07 -0700 Subject: [PATCH 209/363] restyle sanity & screen module to match project style Reduces lint errors from 163 to 112 (-31%). Line count from 1866 to 1493 (-20%). Another step in resolving issue #2902. --- src/sanity.cpp | 51 +- src/sanity.h | 25 +- src/screen.cpp | 1299 +++++++++++++++++++----------------------------- src/screen.h | 290 ++++------- 4 files changed, 646 insertions(+), 1019 deletions(-) diff --git a/src/sanity.cpp b/src/sanity.cpp index 434801266..d6da40f46 100644 --- a/src/sanity.cpp +++ b/src/sanity.cpp @@ -1,60 +1,41 @@ -/** \file sanity.c - Functions for performing sanity checks on the program state -*/ +// Functions for performing sanity checks on the program state. #include -#include "fallback.h" // IWYU pragma: keep #include "common.h" -#include "sanity.h" -#include "proc.h" +#include "fallback.h" // IWYU pragma: keep #include "history.h" -#include "reader.h" #include "kill.h" +#include "proc.h" +#include "reader.h" +#include "sanity.h" -/** - Status from earlier sanity checks -*/ +/// Status from earlier sanity checks. static int insane; -void sanity_lose() -{ +void sanity_lose() { debug(0, _(L"Errors detected, shutting down. Break on sanity_lose() to debug.")); insane = 1; } -int sanity_check() -{ +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 (get_is_interactive()) history_sanity_check(); + if (!insane) reader_sanity_check(); + if (!insane) kill_sanity_check(); + if (!insane) proc_sanity_check(); return insane; } -void validate_pointer(const void *ptr, const wchar_t *err, int null_ok) -{ - - /* - Test if the pointer data crosses a segment boundary. - */ - - if ((0x00000003l & (intptr_t)ptr) != 0) - { +void validate_pointer(const void *ptr, const wchar_t *err, int null_ok) { + // Test if the pointer data crosses a segment boundary. + if ((0x00000003l & (intptr_t)ptr) != 0) { debug(0, _(L"The pointer '%ls' is invalid"), err); sanity_lose(); } - if ((!null_ok) && (ptr==0)) - { + if ((!null_ok) && (ptr == 0)) { debug(0, _(L"The pointer '%ls' is null"), err); sanity_lose(); } } - - diff --git a/src/sanity.h b/src/sanity.h index 7480c403c..6eedb08f0 100644 --- a/src/sanity.h +++ b/src/sanity.h @@ -1,27 +1,18 @@ -/** \file sanity.h - Prototypes for functions for performing sanity checks on the program state -*/ - +// Prototypes for functions for performing sanity checks on the program state. #ifndef FISH_SANITY_H #define FISH_SANITY_H -/** - Call this function to tell the program it is not in a sane state. -*/ +/// Call this function to tell the program it is not in a sane state. void sanity_lose(); -/** - Perform sanity checks, return 1 if program is in a sane state 0 otherwise. -*/ +/// Perform sanity checks, return 1 if program is in a sane state 0 otherwise. int sanity_check(); -/** - Try and determine if ptr is a valid pointer. If not, loose sanity. - - \param ptr The pointer to validate - \param err A description of what the pointer refers to, for use in error messages - \param null_ok Wheter the pointer is allowed to point to 0 -*/ +/// Try and determine if ptr is a valid pointer. If not, loose sanity. +/// +/// \param ptr The pointer to validate +/// \param err A description of what the pointer refers to, for use in error messages +/// \param null_ok Wheter the pointer is allowed to point to 0 void validate_pointer(const void *ptr, const wchar_t *err, int null_ok); #endif diff --git a/src/screen.cpp b/src/screen.cpp index a4d6f4b35..197469424 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -1,15 +1,14 @@ -/** \file screen.c High level library for handling the terminal screen - -The screen library allows the interactive reader to write its -output to screen efficiently by keeping an internal representation -of the current screen contents and trying to find the most -efficient way for transforming that to the desired screen content. -*/ +// High level library for handling the terminal screen. +// +// The screen library allows the interactive reader to write its output to screen efficiently by +// keeping an internal representation of the current screen contents and trying to find the most +// efficient way for transforming that to the desired screen content. +// // IWYU pragma: no_include #include "config.h" -#include #include +#include #include #include #if HAVE_NCURSES_H @@ -24,145 +23,110 @@ efficient way for transforming that to the desired screen content. #elif HAVE_NCURSES_TERM_H #include #endif -#include -#include #include +#include +#include #include #include #include -#include "fallback.h" // IWYU pragma: keep #include "common.h" -#include "util.h" -#include "output.h" -#include "highlight.h" -#include "screen.h" #include "env.h" +#include "fallback.h" // IWYU pragma: keep +#include "highlight.h" +#include "output.h" #include "pager.h" +#include "screen.h" +#include "util.h" -/** The number of characters to indent new blocks */ +/// The number of characters to indent new blocks. #define INDENT_STEP 4u -/** The initial screen width */ +/// The initial screen width. #define SCREEN_WIDTH_UNINITIALIZED -1 -/** A helper value for an invalid location */ +/// A helper value for an invalid location. #define INVALID_LOCATION (screen_data_t::cursor_t(-1, -1)) static void invalidate_soft_wrap(screen_t *scr); -/** - Ugly kludge. The internal buffer used to store output of - tputs. Since tputs external function can only take an integer and - not a pointer as parameter we need a static storage buffer. -*/ +/// Ugly kludge. The internal buffer used to store output of tputs. Since tputs external function +/// can only take an integer and not a pointer as parameter we need a static storage buffer. typedef std::vector data_buffer_t; -static data_buffer_t *s_writeb_buffer=0; +static data_buffer_t *s_writeb_buffer = 0; 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 -{ - data_buffer_t * const old_buff; - int (* const old_writer)(char); +/// Class to temporarily set s_writeb_buffer and the writer function in a scoped way. +class scoped_buffer_t { + data_buffer_t *const old_buff; + int (*const old_writer)(char); -public: - explicit scoped_buffer_t(data_buffer_t *buff) : old_buff(s_writeb_buffer), old_writer(output_get_writer()) - { + public: + explicit scoped_buffer_t(data_buffer_t *buff) + : old_buff(s_writeb_buffer), old_writer(output_get_writer()) { s_writeb_buffer = buff; output_set_writer(s_writeb); } - ~scoped_buffer_t() - { + ~scoped_buffer_t() { s_writeb_buffer = old_buff; output_set_writer(old_writer); } }; -/** - Tests if the specified narrow character sequence is present at the - specified position of the specified wide character string. All of - \c seq must match, but str may be longer than seq. -*/ -static size_t try_sequence(const char *seq, const wchar_t *str) -{ - for (size_t i=0; ; i++) - { - if (!seq[i]) - return i; - - if (seq[i] != str[i]) - return 0; +/// Tests if the specified narrow character sequence is present at the specified position of the +/// specified wide character string. All of \c seq must match, but str may be longer than seq. +static size_t try_sequence(const char *seq, const wchar_t *str) { + for (size_t i = 0;; i++) { + if (!seq[i]) return i; + if (seq[i] != str[i]) 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) -{ - /* - Assume tab stops every 8 characters if undefined - */ +/// 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) { + // 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; + return ((in / tab_width) + 1) * tab_width; } -/* Like fish_wcwidth, but returns 0 for control characters instead of -1 */ -static int fish_wcwidth_min_0(wchar_t wc) -{ - return maxi(0, fish_wcwidth(wc)); -} +/// Like fish_wcwidth, but returns 0 for control characters instead of -1. +static int fish_wcwidth_min_0(wchar_t wc) { return maxi(0, fish_wcwidth(wc)); } -/* Whether we permit soft wrapping. If so, in some cases we don't explicitly move to the second physical line on a wrapped logical line; instead we just output it. */ -static bool allow_soft_wrap(void) -{ +/// Whether we permit soft wrapping. If so, in some cases we don't explicitly move to the second +/// physical line on a wrapped logical line; instead we just output it. +static bool allow_soft_wrap(void) { // Should we be looking at eat_newline_glitch as well? - return !! auto_right_margin; + return !!auto_right_margin; } - -/* Returns the number of characters in the escape code starting at 'code' (which should initially contain \x1b) */ -size_t escape_code_length(const wchar_t *code) -{ +/// Returns the number of characters in the escape code starting at 'code' (which should initially +/// contain \x1b). +size_t escape_code_length(const wchar_t *code) { assert(code != NULL); - /* The only escape codes we recognize start with \x1b */ - if (code[0] != L'\x1b') - return 0; + // The only escape codes we recognize start with \x1b. + if (code[0] != L'\x1b') return 0; size_t resulting_length = 0; bool found = false; - if (cur_term != NULL) - { - /* - 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, + if (cur_term != NULL) { + // 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, }; - for (size_t p=0; p < sizeof esc / sizeof *esc && !found; p++) - { - if (!esc[p]) - continue; + for (size_t p = 0; p < sizeof esc / sizeof *esc && !found; p++) { + if (!esc[p]) continue; - for (size_t k=0; k<8; k++) - { - size_t len = try_sequence(tparm(esc[p],k), code); - if (len) - { + for (size_t k = 0; k < 8; k++) { + size_t len = try_sequence(tparm(esc[p], k), code); + if (len) { resulting_length = len; found = true; break; @@ -171,147 +135,104 @@ size_t escape_code_length(const wchar_t *code) } } - if (cur_term != NULL) - { - /* - 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, - enter_underline_mode, - exit_underline_mode, - enter_standout_mode, - exit_standout_mode, - flash_screen, - enter_subscript_mode, - exit_subscript_mode, - enter_superscript_mode, - exit_superscript_mode, - enter_blink_mode, - enter_italics_mode, - exit_italics_mode, - enter_reverse_mode, - enter_shadow_mode, - exit_shadow_mode, - enter_standout_mode, - exit_standout_mode, - enter_secure_mode - }; + if (cur_term != NULL) { + // 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, enter_underline_mode, + exit_underline_mode, enter_standout_mode, exit_standout_mode, + flash_screen, enter_subscript_mode, exit_subscript_mode, + enter_superscript_mode, exit_superscript_mode, enter_blink_mode, + enter_italics_mode, exit_italics_mode, enter_reverse_mode, + enter_shadow_mode, exit_shadow_mode, enter_standout_mode, + exit_standout_mode, enter_secure_mode}; - - - for (size_t p=0; p < sizeof esc2 / sizeof *esc2 && !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. - */ + for (size_t p = 0; p < sizeof esc2 / sizeof *esc2 && !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. size_t len = maxi(try_sequence(tparm(esc2[p]), code), try_sequence(esc2[p], code)); - if (len) - { + if (len) { resulting_length = len; found = true; } } } - if (!found) - { - if (code[1] == L'k') - { - /* This looks like the escape sequence for setting a screen name */ + if (!found) { + if (code[1] == L'k') { + // This looks like the escape sequence for setting a screen name. const env_var_t term_name = env_get_string(L"TERM"); - if (!term_name.missing() && string_prefixes_string(L"screen", term_name)) - { - const wchar_t * const screen_name_end_sentinel = L"\x1b\\"; + if (!term_name.missing() && string_prefixes_string(L"screen", term_name)) { + const wchar_t *const screen_name_end_sentinel = L"\x1b\\"; const wchar_t *screen_name_end = wcsstr(&code[2], screen_name_end_sentinel); - if (screen_name_end != NULL) - { - const wchar_t *escape_sequence_end = screen_name_end + wcslen(screen_name_end_sentinel); + if (screen_name_end != NULL) { + const wchar_t *escape_sequence_end = + screen_name_end + wcslen(screen_name_end_sentinel); resulting_length = escape_sequence_end - code; - } - else - { - /* Consider just k to be the code */ + } else { + // Consider just k to be the code. resulting_length = 2; } found = true; } } } - - if (! found) - { - /* iTerm2 escape codes: CSI followed by ], terminated by either BEL or escape + backslash. See https://code.google.com/p/iterm2/wiki/ProprietaryEscapeCodes */ - if (code[1] == ']') - { - // Start at 2 to skip over ] + + if (!found) { + // iTerm2 escape codes: CSI followed by ], terminated by either BEL or escape + backslash. + // See https://code.google.com/p/iterm2/wiki/ProprietaryEscapeCodes. + if (code[1] == ']') { + // Start at 2 to skip over ]. size_t cursor = 2; - for (; code[cursor] != L'\0'; cursor++) - { - /* Consume a sequence of characters up to \ or */ - if (code[cursor] == '\x07' || (code[cursor] == '\\' && code[cursor - 1] == '\x1b')) - { + for (; code[cursor] != L'\0'; cursor++) { + // Consume a sequence of characters up to \ or . + if (code[cursor] == '\x07' || + (code[cursor] == '\\' && code[cursor - 1] == '\x1b')) { found = true; break; } } - if (found) - { + if (found) { resulting_length = cursor + 1; } } } - if (! found) - { - /* Generic VT100 one byte sequence: CSI followed by something in the range @ through _ */ - if (code[1] == L'[' && (code[2] >= L'@' && code[2] <= L'_')) - { + if (!found) { + // Generic VT100 one byte sequence: CSI followed by something in the range @ through _. + if (code[1] == L'[' && (code[2] >= L'@' && code[2] <= L'_')) { resulting_length = 3; found = true; } } - if (! found) - { - /* Generic VT100 CSI-style sequence. , followed by zero or more ASCII characters NOT in the range [@,_], followed by one character in that range */ - if (code[1] == L'[') - { + if (!found) { + // Generic VT100 CSI-style sequence. , followed by zero or more ASCII characters NOT in + // the range [@,_], followed by one character in that range. + if (code[1] == L'[') { // Start at 2 to skip over [ size_t cursor = 2; - for (; code[cursor] != L'\0'; cursor++) - { - /* Consume a sequence of ASCII characters not in the range [@, ~] */ + for (; code[cursor] != L'\0'; cursor++) { + // Consume a sequence of ASCII characters not in the range [@, ~]. wchar_t c = code[cursor]; - /* If we're not in ASCII, just stop */ - if (c > 127) - break; + // If we're not in ASCII, just stop. + if (c > 127) break; - /* If we're the end character, then consume it and then stop */ - if (c >= L'@' && c <= L'~') - { + // If we're the end character, then consume it and then stop. + if (c >= L'@' && c <= L'~') { cursor++; break; } } - /* curs now indexes just beyond the end of the sequence (or at the terminating zero) */ + // curs now indexes just beyond the end of the sequence (or at the terminating zero). found = true; resulting_length = cursor; } } - if (! found) - { - /* Generic VT100 two byte sequence: followed by something in the range @ through _ */ - if (code[1] >= L'@' && code[1] <= L'_') - { + if (!found) { + // Generic VT100 two byte sequence: followed by something in the range @ through _. + if (code[1] >= L'@' && code[1] <= L'_') { resulting_length = 2; found = true; } @@ -320,60 +241,45 @@ size_t escape_code_length(const wchar_t *code) return resulting_length; } -/* Information about a prompt layout */ -struct prompt_layout_t -{ - /* How many lines the prompt consumes */ +// Information about a prompt layout. +struct prompt_layout_t { + // How many lines the prompt consumes. size_t line_count; - - /* Width of the longest line */ + // Width of the longest line. size_t max_line_width; - - /* Width of the last line */ + // Width of the last line. size_t last_line_width; }; -/** - Calculate layout information for the given prompt. Does some clever magic - to detect common escape sequences that may be embeded in a prompt, - such as color codes. -*/ -static prompt_layout_t calc_prompt_layout(const wchar_t *prompt) -{ +/// Calculate layout information for the given prompt. Does some clever magic to detect common +/// escape sequences that may be embeded in a prompt, such as color codes. +static prompt_layout_t calc_prompt_layout(const wchar_t *prompt) { size_t current_line_width = 0; size_t j; prompt_layout_t prompt_layout = {}; prompt_layout.line_count = 1; - for (j=0; prompt[j]; j++) - { - if (prompt[j] == L'\x1b') - { - /* This is the start of an escape code. Skip over it if it's at least one character long. */ + for (j = 0; prompt[j]; j++) { + if (prompt[j] == L'\x1b') { + // This is the start of an escape code. Skip over it if it's at least one character + // long. size_t escape_len = escape_code_length(&prompt[j]); - if (escape_len > 0) - { + if (escape_len > 0) { j += escape_len - 1; } - } - else if (prompt[j] == L'\t') - { + } else if (prompt[j] == L'\t') { current_line_width = next_tab_stop(current_line_width); - } - else if (prompt[j] == L'\n' || prompt[j] == L'\f') - { - /* PCA: At least one prompt uses \f\r as a newline. It's unclear to me what this is meant to do, but terminals seem to treat it as a newline so we do the same. */ + } else if (prompt[j] == L'\n' || prompt[j] == L'\f') { + // PCA: At least one prompt uses \f\r as a newline. It's unclear to me what this is + // meant to do, but terminals seem to treat it as a newline so we do the same. current_line_width = 0; prompt_layout.line_count += 1; - } - else if (prompt[j] == L'\r') - { + } else if (prompt[j] == L'\r') { current_line_width = 0; - } - else - { - /* Ordinary decent character. Just add width. This returns -1 for a control character - don't add that. */ + } else { + // Ordinary decent character. Just add width. This returns -1 for a control character - + // don't add that. current_line_width += fish_wcwidth_min_0(prompt[j]); prompt_layout.max_line_width = maxi(prompt_layout.max_line_width, current_line_width); } @@ -382,57 +288,33 @@ static prompt_layout_t calc_prompt_layout(const wchar_t *prompt) return prompt_layout; } -static size_t calc_prompt_lines(const wcstring &prompt) -{ - // Hack for the common case where there's no newline at all - // I don't know if a newline can appear in an escape sequence, - // so if we detect a newline we have to defer to calc_prompt_width_and_lines +static size_t calc_prompt_lines(const wcstring &prompt) { + // Hack for the common case where there's no newline at all. I don't know if a newline can + // appear in an escape sequence, so if we detect a newline we have to defer to + // calc_prompt_width_and_lines. size_t result = 1; - if (prompt.find(L'\n') != wcstring::npos || prompt.find(L'\f') != wcstring::npos) - { + if (prompt.find(L'\n') != wcstring::npos || prompt.find(L'\f') != wcstring::npos) { result = calc_prompt_layout(prompt.c_str()).line_count; } return result; } -/** - Stat stdout and stderr and save result. - - This should be done before calling a function that may cause output. -*/ - -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 +/// Stat stdout and stderr and save result. This should be done before calling a function that may +/// cause output. +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 issue #122. #if 0 - /* - This futimes call tries to trick the system into using st_mtime - as a tampering flag. This of course only works on systems where - futimes is defined, but it should make the status saving stuff - failsafe. - */ - struct timeval t[]= - { - { - time(0)-1, - 0 - } - , - { - time(0)-1, - 0 - } - } - ; + // This futimes call tries to trick the system into using st_mtime as a tampering flag. This of + // course only works on systems where futimes is defined, but it should make the status saving + // stuff failsafe. + struct timeval t[] = { + { time(0)-1, 0 }, + { time(0)-1, 0 } + }; - /* - Don't check return value on these. We don't care if they fail, - really. This is all just to make the prompt look ok, which is - impossible to do 100% reliably. We try, at least. - */ + // 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 @@ -441,16 +323,12 @@ static void s_save_status(screen_t *s) fstat(2, &s->prev_buff_2); } -/** - Stat stdout and stderr and compare result to previous result in - reader_save_status. Repaint if modification time has changed. - - Unfortunately, for some reason this call seems to give a lot of - false positives, at least under Linux. -*/ - -static void s_check_status(screen_t *s) -{ +/// Stat stdout and stderr and compare result to previous result in reader_save_status. Repaint if +/// modification time has changed. +/// +/// Unfortunately, for some reason this call seems to give a lot of false positives, at least under +/// Linux. +static void s_check_status(screen_t *s) { fflush(stdout); fflush(stderr); @@ -460,25 +338,20 @@ static void s_check_status(screen_t *s) 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 defined HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC - changed = changed || s->prev_buff_1.st_mtimespec.tv_nsec != s->post_buff_1.st_mtimespec.tv_nsec || - s->prev_buff_2.st_mtimespec.tv_nsec != s->post_buff_2.st_mtimespec.tv_nsec; - #elif defined HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC - changed = changed || s->prev_buff_1.st_mtim.tv_nsec != s->post_buff_1.st_mtim.tv_nsec || - s->prev_buff_2.st_mtim.tv_nsec != s->post_buff_2.st_mtim.tv_nsec; - #endif - - 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 defined HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC + changed = changed || + s->prev_buff_1.st_mtimespec.tv_nsec != s->post_buff_1.st_mtimespec.tv_nsec || + s->prev_buff_2.st_mtimespec.tv_nsec != s->post_buff_2.st_mtimespec.tv_nsec; +#elif defined HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC + changed = changed || s->prev_buff_1.st_mtim.tv_nsec != s->post_buff_1.st_mtim.tv_nsec || + s->prev_buff_2.st_mtim.tv_nsec != s->post_buff_2.st_mtim.tv_nsec; +#endif + 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(STDOUT_FILENO, "\r", 1); s_reset(s, screen_reset_current_line_and_prompt); @@ -486,73 +359,55 @@ static void s_check_status(screen_t *s) } } -/** - Appends a character to the end of the line that the output cursor is - on. This function automatically handles linebreaks and lines longer - than the screen width. -*/ -static void s_desired_append_char(screen_t *s, - wchar_t b, - int c, - int indent, - size_t prompt_width) -{ +/// Appends a character to the end of the line that the output cursor is on. This function +/// automatically handles linebreaks and lines longer than the screen width. +static void s_desired_append_char(screen_t *s, wchar_t b, int c, int indent, size_t prompt_width) { int line_no = s->desired.cursor.y; - switch (b) - { - case L'\n': - { + switch (b) { + case L'\n': { int i; - /* Current line is definitely hard wrapped */ + // Current line is definitely hard wrapped. s->desired.create_line(s->desired.line_count()); s->desired.line(s->desired.cursor.y).is_soft_wrapped = false; s->desired.cursor.y++; - s->desired.cursor.x=0; - for (i=0; i < prompt_width+indent*INDENT_STEP; i++) - { + 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': - { + case L'\r': { line_t ¤t = s->desired.line(line_no); current.clear(); s->desired.cursor.x = 0; break; } - - default: - { + default: { int screen_width = common_get_width(); int cw = fish_wcwidth_min_0(b); s->desired.create_line(line_no); - /* - Check if we are at the end of the line. If so, continue on the next line. - */ - if ((s->desired.cursor.x + cw) > screen_width) - { - /* Current line is soft wrapped (assuming we support it) */ + // 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); + // fprintf(stderr, "\n\n1 Soft wrapping %d\n\n", s->desired.cursor.y); line_no = (int)s->desired.line_count(); s->desired.add_line(); s->desired.cursor.y++; - s->desired.cursor.x=0; + s->desired.cursor.x = 0; } line_t &line = s->desired.line(line_no); line.append(b, c); - s->desired.cursor.x+= cw; + s->desired.cursor.x += cw; - /* Maybe wrap the cursor to the next line, even if the line itself did not wrap. This avoids wonkiness in the last column. */ - if (s->desired.cursor.x >= screen_width) - { + // 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++; @@ -560,47 +415,36 @@ static void s_desired_append_char(screen_t *s, break; } } - } -/** - The writeb function offered to tputs. -*/ -static int s_writeb(char c) -{ +/// The writeb function offered to tputs. +static int s_writeb(char c) { s_writeb_buffer->push_back(c); return 0; } -/** - Write the bytes needed to move screen cursor to the specified - position to the specified buffer. The actual_cursor field of the - specified screen_t will be updated. +/// Write the bytes needed to move screen cursor to the specified position to the specified buffer. +/// The actual_cursor field of the specified screen_t will be updated. +/// +/// \param s the screen to operate on +/// \param b the buffer to send the output escape codes to +/// \param new_x the new x position +/// \param new_y the new y position +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; - \param s the screen to operate on - \param b the buffer to send the output escape codes to - \param new_x the new x position - \param new_y the new y position -*/ -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; - - // If we are at the end of our window, then either the cursor stuck to the edge or it didn't. We don't know! We can fix it up though. - if (s->actual.cursor.x == common_get_width()) - { - // Either issue a cr to go back to the beginning of this line, or a nl to go to the beginning of the next one, depending on what we think is more efficient - if (new_y <= s->actual.cursor.y) - { + // If we are at the end of our window, then either the cursor stuck to the edge or it didn't. We + // don't know! We can fix it up though. + if (s->actual.cursor.x == common_get_width()) { + // Either issue a cr to go back to the beginning of this line, or a nl to go to the + // beginning of the next one, depending on what we think is more efficient. + if (new_y <= s->actual.cursor.y) { b->push_back('\r'); - } - else - { + } else { b->push_back('\n'); s->actual.cursor.y++; } - // Either way we're not in the first column + // Either way we're not in the first column. s->actual.cursor.x = 0; } @@ -617,174 +461,135 @@ static void s_move(screen_t *s, data_buffer_t *b, int new_x, int new_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 && (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) - { + if (y_steps < 0) { str = cursor_up; - } - else - { + } else { str = cursor_down; - } - for (i=0; iactual.cursor.x; - if (x_steps && new_x == 0) - { + if (x_steps && new_x == 0) { b->push_back('\r'); x_steps = 0; } char *multi_str = NULL; - if (x_steps < 0) - { + if (x_steps < 0) { str = cursor_left; multi_str = parm_left_cursor; - } - else - { + } else { str = cursor_right; multi_str = parm_right_cursor; } - + // Use the bulk ('multi') output for cursor movement if it is supported and it would be shorter - // Note that this is required to avoid some visual glitches in iTerm (#1448) - bool use_multi = (multi_str != NULL && multi_str[0] != '\0' && abs(x_steps) * strlen(str) > strlen(multi_str)); - if (use_multi) - { + // Note that this is required to avoid some visual glitches in iTerm (issue #1448). + bool use_multi = (multi_str != NULL && multi_str[0] != '\0' && + abs(x_steps) * strlen(str) > strlen(multi_str)); + if (use_multi) { char *multi_param = tparm(multi_str, abs(x_steps)); writembs(multi_param); - } - else - { - 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, highlight_spec_t c) -{ +/// Set the pen color for the terminal. +static void s_set_color(screen_t *s, data_buffer_t *b, highlight_spec_t 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)); + 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) -{ +/// 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) { scoped_buffer_t scoped_buffer(b); s->actual.cursor.x += fish_wcwidth_min_0(c); writech(c); - if (s->actual.cursor.x == s->actual_width && allow_soft_wrap()) - { + if (s->actual.cursor.x == s->actual_width && allow_soft_wrap()) { s->soft_wrap_location.x = 0; s->soft_wrap_location.y = s->actual.cursor.y + 1; - /* Note that our cursor position may be a lie: Apple Terminal makes the right cursor stick to the margin, while Ubuntu makes it "go off the end" (but still doesn't wrap). We rely on s_move to fix this up. */ - } - else - { + // Note that our cursor position may be a lie: Apple Terminal makes the right cursor stick + // to the margin, while Ubuntu makes it "go off the end" (but still doesn't wrap). We rely + // on s_move to fix this up. + } else { invalidate_soft_wrap(s); } } -/** - 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) -{ +/// 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) { scoped_buffer_t scoped_buffer(b); 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) -{ +/// 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) { scoped_buffer_t scoped_buffer(b); writestr(s); } -/** Returns the length of the "shared prefix" of the two lines, which is the run of matching text and colors. - If the prefix ends on a combining character, do not include the previous character in the prefix. -*/ -static size_t line_shared_prefix(const line_t &a, const line_t &b) -{ +/// Returns the length of the "shared prefix" of the two lines, which is the run of matching text +/// and colors. If the prefix ends on a combining character, do not include the previous character +/// in the prefix. +static size_t line_shared_prefix(const line_t &a, const line_t &b) { size_t idx, max = std::min(a.size(), b.size()); - for (idx=0; idx < max; idx++) - { + for (idx = 0; idx < max; idx++) { wchar_t ac = a.char_at(idx), bc = b.char_at(idx); - if (fish_wcwidth(ac) < 1 || fish_wcwidth(bc) < 1) - { - /* Possible combining mark, return one index prior */ + if (fish_wcwidth(ac) < 1 || fish_wcwidth(bc) < 1) { + // Possible combining mark, return one index prior. if (idx > 0) idx--; break; } - /* We're done if the text or colors are different */ - if (ac != bc || a.color_at(idx) != b.color_at(idx)) - break; + // We're done if the text or colors are different. + if (ac != bc || a.color_at(idx) != b.color_at(idx)) break; } return idx; } -/* We are about to output one or more characters onto the screen at the given x, y. If we are at the end of previous line, and the previous line is marked as soft wrapping, then tweak the screen so we believe we are already in the target position. This lets the terminal take care of wrapping, which means that if you copy and paste the text, it won't have an embedded newline. */ -static bool perform_any_impending_soft_wrap(screen_t *scr, int x, int y) -{ - if (x == scr->soft_wrap_location.x && y == scr->soft_wrap_location.y) - { - /* We can soft wrap; but do we want to? */ - if (scr->desired.line(y - 1).is_soft_wrapped && allow_soft_wrap()) - { - /* Yes. Just update the actual cursor; that will cause us to elide emitting the commands to move here, so we will just output on "one big line" (which the terminal soft wraps */ +// We are about to output one or more characters onto the screen at the given x, y. If we are at the +// end of previous line, and the previous line is marked as soft wrapping, then tweak the screen so +// we believe we are already in the target position. This lets the terminal take care of wrapping, +// which means that if you copy and paste the text, it won't have an embedded newline. +static bool perform_any_impending_soft_wrap(screen_t *scr, int x, int y) { + if (x == scr->soft_wrap_location.x && y == scr->soft_wrap_location.y) { + // We can soft wrap; but do we want to? + if (scr->desired.line(y - 1).is_soft_wrapped && allow_soft_wrap()) { + // Yes. Just update the actual cursor; that will cause us to elide emitting the commands + // to move here, so we will just output on "one big line" (which the terminal soft + // wraps. scr->actual.cursor = scr->soft_wrap_location; } } return false; } -/* Make sure we don't soft wrap */ -static void invalidate_soft_wrap(screen_t *scr) -{ - scr->soft_wrap_location = INVALID_LOCATION; -} +/// Make sure we don't soft wrap. +static void invalidate_soft_wrap(screen_t *scr) { scr->soft_wrap_location = INVALID_LOCATION; } + +// Various code for testing term behavior. -/* Various code for testing term behavior */ #if 0 static bool test_stuff(screen_t *scr) { @@ -842,18 +647,15 @@ static bool test_stuff(screen_t *scr) } #endif -/** - 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) -{ - //if (test_stuff(scr)) return; +/// 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) { + // if (test_stuff(scr)) return; const size_t left_prompt_width = calc_prompt_layout(left_prompt).last_line_width; const size_t right_prompt_width = calc_prompt_layout(right_prompt).last_line_width; int screen_width = common_get_width(); - /* Figure out how many following lines we need to clear (probably 0) */ + // 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; @@ -863,11 +665,9 @@ static void s_update(screen_t *scr, const wchar_t *left_prompt, const wchar_t *r bool need_clear_screen = scr->need_clear_screen; bool has_cleared_screen = false; - if (scr->actual_width != screen_width) - { - /* Ensure we don't issue a clear screen for the very first output, to avoid https://github.com/fish-shell/fish-shell/issues/402 */ - if (scr->actual_width != SCREEN_WIDTH_UNINITIALIZED) - { + if (scr->actual_width != screen_width) { + // Ensure we don't issue a clear screen for the very first output, to avoid issue #402. + if (scr->actual_width != SCREEN_WIDTH_UNINITIALIZED) { need_clear_screen = true; s_move(scr, &output, 0, 0); s_reset(scr, screen_reset_current_line_contents); @@ -881,89 +681,81 @@ static void s_update(screen_t *scr, const wchar_t *left_prompt, const wchar_t *r scr->need_clear_lines = false; scr->need_clear_screen = false; - /* Determine how many lines have stuff on them; we need to clear lines with stuff that we don't want */ + // Determine how many lines have stuff on them; we need to clear lines with stuff that we don't + // want. const size_t lines_with_stuff = maxi(actual_lines_before_reset, scr->actual.line_count()); - if (lines_with_stuff > scr->desired.line_count()) - { - /* There are lines that we output to previously that will need to be cleared */ - //need_clear_lines = true; + if (lines_with_stuff > scr->desired.line_count()) { + // There are lines that we output to previously that will need to be cleared. + // need_clear_lines = true; } - if (wcscmp(left_prompt, scr->actual_left_prompt.c_str())) - { + 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; } - for (size_t i=0; i < scr->desired.line_count(); i++) - { + 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); + size_t start_pos = (i == 0 ? left_prompt_width : 0); int current_width = 0; - /* If this is the last line, maybe we should clear the screen */ - const bool should_clear_screen_this_line = (need_clear_screen && i + 1 == scr->desired.line_count() && clr_eos != NULL); + // If this is the last line, maybe we should clear the screen. + const bool should_clear_screen_this_line = + (need_clear_screen && i + 1 == scr->desired.line_count() && clr_eos != NULL); - /* Note that skip_remaining is a width, not a character count */ + // Note that skip_remaining is a width, not a character count. size_t skip_remaining = start_pos; - if (! should_clear_screen_this_line) - { - /* Compute how much we should skip. At a minimum we skip over the prompt. But also skip over the shared prefix of what we want to output now, and what we output before, to avoid repeatedly outputting it. */ + 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) - { + if (shared_prefix > 0) { int prefix_width = fish_wcswidth(&o_line.text.at(0), shared_prefix); - if (prefix_width > skip_remaining) - skip_remaining = prefix_width; + if (prefix_width > skip_remaining) skip_remaining = prefix_width; } - /* If we're soft wrapped, and if we're going to change the first character of the next line, don't skip over the last two characters so that we maintain soft-wrapping */ - if (o_line.is_soft_wrapped && i + 1 < scr->desired.line_count()) - { + // If we're soft wrapped, and if we're going to change the first character of the next + // line, don't skip over the last two characters so that we maintain soft-wrapping. + if (o_line.is_soft_wrapped && i + 1 < scr->desired.line_count()) { bool first_character_of_next_line_will_change = true; - if (i + 1 < scr->actual.line_count()) - { - if (line_shared_prefix(scr->desired.line(i+1), scr->actual.line(i+1)) > 0) - { + if (i + 1 < scr->actual.line_count()) { + if (line_shared_prefix(scr->desired.line(i + 1), scr->actual.line(i + 1)) > 0) { first_character_of_next_line_will_change = false; } } - if (first_character_of_next_line_will_change) - { + if (first_character_of_next_line_will_change) { skip_remaining = mini(skip_remaining, (size_t)(scr->actual_width - 2)); } } } - /* Skip over skip_remaining width worth of characters */ + // 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_min_0(o_line.char_at(j)); - if (skip_remaining < width) - break; + if (skip_remaining < width) break; skip_remaining -= width; current_width += width; } - /* Skip over zero-width characters (e.g. combining marks at the end of the prompt) */ - for (; j < o_line.size(); j++) - { + // Skip over zero-width characters (e.g. combining marks at the end of the prompt). + for (; j < o_line.size(); j++) { int width = fish_wcwidth_min_0(o_line.char_at(j)); - if (width > 0) - break; + if (width > 0) break; } - /* Now actually output stuff */ - for (; j < o_line.size(); j++) - { - /* If we are about to output into the last column, clear the screen first. If we clear the screen after we output into the last column, it can erase the last character due to the sticky right cursor. If we clear the screen too early, we can defeat soft wrapping. */ - if (j + 1 == screen_width && should_clear_screen_this_line && ! has_cleared_screen) - { + // Now actually output stuff. + for (; j < o_line.size(); j++) { + // If we are about to output into the last column, clear the screen first. If we clear + // the screen after we output into the last column, it can erase the last character due + // to the sticky right cursor. If we clear the screen too early, we can defeat soft + // wrapping. + if (j + 1 == screen_width && should_clear_screen_this_line && !has_cleared_screen) { s_move(scr, &output, current_width, (int)i); s_write_mbs(&output, clr_eos); has_cleared_screen = true; @@ -976,68 +768,61 @@ static void s_update(screen_t *scr, const wchar_t *left_prompt, const wchar_t *r current_width += fish_wcwidth_min_0(o_line.char_at(j)); } - /* Clear the screen if we have not done so yet. */ - if (should_clear_screen_this_line && ! has_cleared_screen) - { + // Clear the screen if we have not done so yet. + if (should_clear_screen_this_line && !has_cleared_screen) { s_move(scr, &output, current_width, (int)i); s_write_mbs(&output, clr_eos); has_cleared_screen = true; } bool clear_remainder = false; - /* Clear the remainder of the line if we need to clear and if we didn't write to the end of the line. If we did write to the end of the line, the "sticky right edge" (as part of auto_right_margin) means that we'll be clearing the last character we wrote! */ - if (has_cleared_screen) - { - /* Already cleared everything */ + // Clear the remainder of the line if we need to clear and if we didn't write to the end of + // the line. If we did write to the end of the line, the "sticky right edge" (as part of + // auto_right_margin) means that we'll be clearing the last character we wrote! + if (has_cleared_screen) { + // Already cleared everything. clear_remainder = false; - } - else if (need_clear_lines && current_width < screen_width) - { + } else if (need_clear_lines && current_width < screen_width) { clear_remainder = true; - } - else if (right_prompt_width < scr->last_right_prompt_width) - { + } else if (right_prompt_width < scr->last_right_prompt_width) { clear_remainder = true; - } - else - { - int prev_width = (s_line.text.empty() ? 0 : fish_wcswidth(&s_line.text.at(0), s_line.text.size())); + } else { + int prev_width = + (s_line.text.empty() ? 0 : fish_wcswidth(&s_line.text.at(0), s_line.text.size())); clear_remainder = prev_width > current_width; - } - if (clear_remainder) - { + if (clear_remainder) { s_set_color(scr, &output, 0xffffffff); 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) - { + // 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); scr->actual.cursor.x += right_prompt_width; - /* 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. - - However, if the user is resizing the window smaller, then it's possible the cursor wrapped. If so, then a cr will go to the beginning of the following line! So instead issue a bunch of "move left" commands to get back onto the line, and then jump to the front of it (!) - */ - - s_move(scr, &output, scr->actual.cursor.x - (int)right_prompt_width, scr->actual.cursor.y); + // 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. + // + // However, if the user is resizing the window smaller, then it's possible the cursor + // wrapped. If so, then a cr will go to the beginning of the following line! So instead + // issue a bunch of "move left" commands to get back onto the line, and then jump to the + // front of it. + s_move(scr, &output, scr->actual.cursor.x - (int)right_prompt_width, + scr->actual.cursor.y); s_write_str(&output, L"\r"); scr->actual.cursor.x = 0; } } - - /* Clear remaining lines (if any) if we haven't cleared the screen. */ - if (! has_cleared_screen && scr->desired.line_count() < lines_with_stuff) - { + // Clear remaining lines (if any) if we haven't cleared the screen. + if (!has_cleared_screen && scr->desired.line_count() < lines_with_stuff) { s_set_color(scr, &output, 0xffffffff); - 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); } @@ -1046,65 +831,54 @@ static void s_update(screen_t *scr, const wchar_t *left_prompt, const wchar_t *r s_move(scr, &output, scr->desired.cursor.x, scr->desired.cursor.y); s_set_color(scr, &output, 0xffffffff); - if (! output.empty()) - { + if (!output.empty()) { write_loop(STDOUT_FILENO, &output.at(0), output.size()); } - /* We have now synced our actual screen against our desired screen. Note that this is a big assignment! */ + // We have now synced our actual screen against our desired screen. Note that this is a big + // assignment! scr->actual = scr->desired; scr->last_right_prompt_width = right_prompt_width; } -/** Returns true if we are using a dumb terminal. */ -static bool is_dumb(void) -{ - return (!cursor_up || !cursor_down || !cursor_left || !cursor_right); -} +/// Returns true if we are using a dumb terminal. +static bool is_dumb(void) { return (!cursor_up || !cursor_down || !cursor_left || !cursor_right); } -struct screen_layout_t -{ - /* The left prompt that we're going to use */ +struct screen_layout_t { + // The left prompt that we're going to use. wcstring left_prompt; - - /* How much space to leave for it */ + // How much space to leave for it. size_t left_prompt_space; - - /* The right prompt */ + // The right prompt. wcstring right_prompt; - - /* The autosuggestion */ + // The autosuggestion. wcstring autosuggestion; - - /* Whether the prompts get their own line or not */ + // Whether the prompts get their own line or not. bool prompts_get_own_line; }; -/* Given a vector whose indexes are offsets and whose values are the widths of the string if truncated at that offset, return the offset that fits in the given width. Returns width_by_offset.size() - 1 if they all fit. The first value in width_by_offset is assumed to be 0. */ -static size_t truncation_offset_for_width(const std::vector &width_by_offset, size_t max_width) -{ - assert(! width_by_offset.empty() && width_by_offset.at(0) == 0); +// Given a vector whose indexes are offsets and whose values are the widths of the string if +// truncated at that offset, return the offset that fits in the given width. Returns +// width_by_offset.size() - 1 if they all fit. The first value in width_by_offset is assumed to be +// 0. +static size_t truncation_offset_for_width(const std::vector &width_by_offset, + size_t max_width) { + assert(!width_by_offset.empty() && width_by_offset.at(0) == 0); size_t i; - for (i=1; i < width_by_offset.size(); i++) - { - if (width_by_offset.at(i) > max_width) - break; + for (i = 1; i < width_by_offset.size(); i++) { + if (width_by_offset.at(i) > max_width) break; } - /* i is the first index that did not fit; i-1 is therefore the last that did */ + // i is the first index that did not fit; i-1 is therefore the last that did. return i - 1; } -static screen_layout_t compute_layout(screen_t *s, - size_t screen_width, +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) -{ + const wcstring &right_prompt_str, const wcstring &commandline, + const wcstring &autosuggestion_str, const int *indent) { screen_layout_t result = {}; - /* Start by ensuring that the prompts themselves can fit */ + // Start by ensuring that the prompts themselves can fit. const wchar_t *left_prompt = left_prompt_str.c_str(); const wchar_t *right_prompt = right_prompt_str.c_str(); const wchar_t *autosuggestion = autosuggestion_str.c_str(); @@ -1115,83 +889,81 @@ static screen_layout_t compute_layout(screen_t *s, size_t left_prompt_width = left_prompt_layout.last_line_width; size_t right_prompt_width = right_prompt_layout.last_line_width; - if (left_prompt_layout.max_line_width > screen_width) - { - /* If we have a multi-line prompt, see if the longest line fits; if not neuter the whole left prompt */ + if (left_prompt_layout.max_line_width > screen_width) { + // If we have a multi-line prompt, see if the longest line fits; if not neuter the whole + // left prompt. left_prompt = L"> "; left_prompt_width = 2; } - if (left_prompt_width + right_prompt_width >= screen_width) - { - /* Nix right_prompt */ + if (left_prompt_width + right_prompt_width >= screen_width) { + // Nix right_prompt. right_prompt = L""; right_prompt_width = 0; } - if (left_prompt_width + right_prompt_width >= screen_width) - { - /* Still doesn't fit, neuter left_prompt */ + if (left_prompt_width + right_prompt_width >= screen_width) { + // Still doesn't fit, neuter left_prompt. left_prompt = L"> "; left_prompt_width = 2; } - /* Now we should definitely fit */ + // Now we should definitely fit. assert(left_prompt_width + right_prompt_width < screen_width); - - /* Convert commandline to a list of lines and their widths */ + // Convert commandline to a list of lines and their widths. wcstring_list_t command_lines(1); std::vector line_widths(1); - for (size_t i=0; i < commandline.size(); i++) - { + for (size_t i = 0; i < commandline.size(); i++) { wchar_t c = commandline.at(i); - if (c == L'\n') - { - /* Make a new line */ + if (c == L'\n') { + // Make a new line. command_lines.push_back(wcstring()); - line_widths.push_back(indent[i]*INDENT_STEP); - } - else - { + line_widths.push_back(indent[i] * INDENT_STEP); + } else { command_lines.back() += c; line_widths.back() += fish_wcwidth_min_0(c); } } const size_t first_command_line_width = line_widths.at(0); - /* If we have more than one line, ensure we have no autosuggestion */ - if (command_lines.size() > 1) - { + // If we have more than one line, ensure we have no autosuggestion. + if (command_lines.size() > 1) { autosuggestion = L""; } - /* Compute the width of the autosuggestion at all possible truncation offsets */ + // Compute the width of the autosuggestion at all possible truncation offsets. std::vector autosuggestion_truncated_widths; autosuggestion_truncated_widths.reserve(1 + wcslen(autosuggestion)); size_t autosuggestion_total_width = 0; - for (size_t i=0; autosuggestion[i] != L'\0'; i++) - { + for (size_t i = 0; autosuggestion[i] != L'\0'; i++) { autosuggestion_truncated_widths.push_back(autosuggestion_total_width); autosuggestion_total_width += fish_wcwidth_min_0(autosuggestion[i]); } - /* Here are the layouts we try in turn: - - 1. Left prompt visible, right prompt visible, command line visible, autosuggestion visible - 2. Left prompt visible, right prompt visible, command line visible, autosuggestion truncated (possibly to zero) - 3. Left prompt visible, right prompt hidden, command line visible, autosuggestion hidden - 4. Newline separator (left prompt visible, right prompt hidden, command line visible, autosuggestion visible) - - A remark about layout #4: if we've pushed the command line to a new line, why can't we draw the right prompt? The issue is resizing: if you resize the window smaller, then the right prompt will wrap to the next line. This means that we can't go back to the line that we were on, and things turn to chaos very quickly. - - */ - + // Here are the layouts we try in turn: + // + // 1. Left prompt visible, right prompt visible, command line visible, autosuggestion visible. + // + // 2. Left prompt visible, right prompt visible, command line visible, autosuggestion truncated + // (possibly to zero). + // + // 3. Left prompt visible, right prompt hidden, command line visible, autosuggestion hidden. + // + // 4. Newline separator (left prompt visible, right prompt hidden, command line visible, + // autosuggestion visible). + // + // A remark about layout #4: if we've pushed the command line to a new line, why can't we draw + // the right prompt? The issue is resizing: if you resize the window smaller, then the right + // prompt will wrap to the next line. This means that we can't go back to the line that we were + // on, and things turn to chaos very quickly. bool done = false; - /* Case 1 */ - if (! done && left_prompt_width + right_prompt_width + first_command_line_width + autosuggestion_total_width < screen_width) - { + // Case 1 + if (!done && + left_prompt_width + right_prompt_width + first_command_line_width + + autosuggestion_total_width < + screen_width) { result.left_prompt = left_prompt; result.left_prompt_space = left_prompt_width; result.right_prompt = right_prompt; @@ -1199,45 +971,42 @@ static screen_layout_t compute_layout(screen_t *s, done = true; } - /* Case 2. Note that we require strict inequality so that there's always at least one space between the left edge and the rprompt */ - if (! done && left_prompt_width + right_prompt_width + first_command_line_width < screen_width) - { + // Case 2. Note that we require strict inequality so that there's always at least one space + // between the left edge and the rprompt. + if (!done && left_prompt_width + right_prompt_width + first_command_line_width < screen_width) { result.left_prompt = left_prompt; result.left_prompt_space = left_prompt_width; result.right_prompt = right_prompt; - /* Need at least two characters to show an autosuggestion */ - size_t available_autosuggestion_space = screen_width - (left_prompt_width + right_prompt_width + first_command_line_width); - if (autosuggestion_total_width > 0 && available_autosuggestion_space > 2) - { - size_t truncation_offset = truncation_offset_for_width(autosuggestion_truncated_widths, available_autosuggestion_space - 2); + // Need at least two characters to show an autosuggestion. + size_t available_autosuggestion_space = + screen_width - (left_prompt_width + right_prompt_width + first_command_line_width); + if (autosuggestion_total_width > 0 && available_autosuggestion_space > 2) { + size_t truncation_offset = truncation_offset_for_width( + autosuggestion_truncated_widths, available_autosuggestion_space - 2); result.autosuggestion = wcstring(autosuggestion, truncation_offset); result.autosuggestion.push_back(ellipsis_char); } done = true; } - /* Case 3 */ - if (! done && left_prompt_width + first_command_line_width < screen_width) - { + // Case 3 + if (!done && left_prompt_width + first_command_line_width < screen_width) { result.left_prompt = left_prompt; result.left_prompt_space = left_prompt_width; done = true; } - /* Case 4 */ - if (! done) - { + // Case 4 + if (!done) { result.left_prompt = left_prompt; result.left_prompt_space = left_prompt_width; - // See remark about for why we can't use the right prompt here - //result.right_prompt = right_prompt; - - // If the command wraps, and the prompt is not short, place the command on its own line. - // A short prompt is 33% or less of the terminal's width. + // See remark about for why we can't use the right prompt here result.right_prompt = + // right_prompt. If the command wraps, and the prompt is not short, place the command on its + // own line. A short prompt is 33% or less of the terminal's width. const size_t prompt_percent_width = (100 * left_prompt_width) / screen_width; - if (left_prompt_width + first_command_line_width + 1 > screen_width && prompt_percent_width > 33) - { + if (left_prompt_width + first_command_line_width + 1 > screen_width && + prompt_percent_width > 33) { result.prompts_get_own_line = true; } @@ -1248,35 +1017,22 @@ static screen_layout_t compute_layout(screen_t *s, return result; } - -void s_write(screen_t *s, - const wcstring &left_prompt, - const wcstring &right_prompt, - const wcstring &commandline, - size_t explicit_len, - const highlight_spec_t *colors, - const int *indent, - size_t cursor_pos, - size_t sel_start_pos, - size_t sel_stop_pos, - const page_rendering_t &pager, - bool cursor_position_is_within_pager) -{ +void s_write(screen_t *s, const wcstring &left_prompt, const wcstring &right_prompt, + const wcstring &commandline, size_t explicit_len, const highlight_spec_t *colors, + const int *indent, size_t cursor_pos, size_t sel_start_pos, size_t sel_stop_pos, + const page_rendering_t &pager, bool cursor_position_is_within_pager) { 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 */ + // 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()) - { + // 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); @@ -1290,168 +1046,162 @@ void s_write(screen_t *s, s_check_status(s); const size_t screen_width = common_get_width(); - /* Completely ignore impossibly small screens */ - if (screen_width < 4) - { + // 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); + // Compute a layout. + const screen_layout_t layout = compute_layout(s, screen_width, left_prompt, right_prompt, + explicit_command_line, autosuggestion, indent); - /* Determine whether, if we have an autosuggestion, it was truncated */ - s->autosuggestion_is_truncated = ! autosuggestion.empty() && autosuggestion != layout.autosuggestion; + // Determine whether, if we have an autosuggestion, it was truncated. + s->autosuggestion_is_truncated = + !autosuggestion.empty() && autosuggestion != layout.autosuggestion; - /* Clear the desired screen */ + // Clear the desired screen. s->desired.resize(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++) - { + // Append spaces for the left prompt. + for (size_t i = 0; i < layout.left_prompt_space; i++) { s_desired_append_char(s, L' ', 0, 0, layout.left_prompt_space); } - /* If overflowing, give the prompt its own line to improve the situation. */ + // 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) - { + if (layout.prompts_get_own_line) { s_desired_append_char(s, L'\n', 0, 0, 0); first_line_prompt_space = 0; } - /* Reconstruct the command line */ + // Reconstruct the command line. wcstring effective_commandline = explicit_command_line + layout.autosuggestion; - /* Output the command line */ + // Output the command line. size_t i; - for (i=0; i < effective_commandline.size(); i++) - { - /* Grab the current cursor's x,y position if this character matches the cursor's offset */ - if (! cursor_position_is_within_pager && i == cursor_pos) - { + for (i = 0; i < effective_commandline.size(); i++) { + // Grab the current cursor's x,y position if this character matches the cursor's offset. + if (!cursor_position_is_within_pager && i == cursor_pos) { cursor_arr = s->desired.cursor; } - s_desired_append_char(s, effective_commandline.at(i), colors[i], indent[i], first_line_prompt_space); + s_desired_append_char(s, effective_commandline.at(i), colors[i], indent[i], + first_line_prompt_space); } - - /* Cursor may have been at the end too */ - if (! cursor_position_is_within_pager && i == cursor_pos) - { + + // Cursor may have been at the end too. + if (!cursor_position_is_within_pager && i == cursor_pos) { cursor_arr = s->desired.cursor; } - - /* Now that we've output everything, set the cursor to the position that we saved in the loop above */ + + // Now that we've output everything, set the cursor to the position that we saved in the loop + // above. s->desired.cursor = cursor_arr; - if (cursor_position_is_within_pager) - { + if (cursor_position_is_within_pager) { s->desired.cursor.x = (int)cursor_pos; s->desired.cursor.y = (int)s->desired.line_count(); } - /* Append pager_data (none if empty) */ + // Append pager_data (none if empty). s->desired.append_lines(pager.screen_data); s_update(s, layout.left_prompt.c_str(), layout.right_prompt.c_str()); s_save_status(s); } -void s_reset(screen_t *s, screen_reset_mode_t mode) -{ - CHECK(s,); +void s_reset(screen_t *s, screen_reset_mode_t mode) { + CHECK(s, ); bool abandon_line = false, repaint_prompt = false, clear_to_eos = false; - switch (mode) - { - case screen_reset_current_line_contents: + switch (mode) { + case screen_reset_current_line_contents: { break; - - case screen_reset_current_line_and_prompt: + } + case screen_reset_current_line_and_prompt: { repaint_prompt = true; break; - - case screen_reset_abandon_line: + } + case screen_reset_abandon_line: { abandon_line = true; repaint_prompt = true; break; - - case screen_reset_abandon_line_and_clear_to_end_of_screen: + } + case screen_reset_abandon_line_and_clear_to_end_of_screen: { abandon_line = true; repaint_prompt = true; clear_to_eos = true; break; + } } - /* If we're abandoning the line, we must also be repainting the prompt */ - assert(! abandon_line || repaint_prompt); + // If we're abandoning the line, we must also be repainting the prompt. + assert(!abandon_line || repaint_prompt); - /* If we are not abandoning the line, 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 (! abandon_line) - { - s->actual_lines_before_reset = maxi(s->actual_lines_before_reset, s->actual.line_count()); + // If we are not abandoning the line, 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 (!abandon_line) { + s->actual_lines_before_reset = maxi(s->actual_lines_before_reset, s->actual.line_count()); } - if (repaint_prompt && ! abandon_line) - { - - /* 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 us to move up to try to get back to line 0, but really we're getting back to the initial line of the prompt. */ + if (repaint_prompt && !abandon_line) { + // 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 us 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); assert(prompt_line_count >= 1); s->actual.cursor.y += (prompt_line_count - 1); - } - else if (abandon_line) - { + } else if (abandon_line) { s->actual.cursor.y = 0; } - if (repaint_prompt) - s->actual_left_prompt.clear(); + if (repaint_prompt) s->actual_left_prompt.clear(); s->actual.resize(0); s->need_clear_lines = true; s->need_clear_screen = s->need_clear_screen || clear_to_eos; - if (abandon_line) - { - /* Do the PROMPT_SP hack */ + if (abandon_line) { + // Do the PROMPT_SP hack. int screen_width = common_get_width(); wcstring abandon_line_string; - abandon_line_string.reserve(screen_width + 32); //should be enough + abandon_line_string.reserve(screen_width + 32); // should be enough - /* Don't need to check for wcwidth errors; this is done when setting up omitted_newline_char in common.cpp */ + // Don't need to check for wcwidth errors; this is done when setting up omitted_newline_char + // in common.cpp. int non_space_width = wcwidth(omitted_newline_char); - if (screen_width >= non_space_width) - { - if (output_get_color_support() & color_support_term256) - { - // draw the string in term256 gray + if (screen_width >= non_space_width) { + if (output_get_color_support() & color_support_term256) { + // Draw the string in term256 gray. abandon_line_string.append(L"\x1b[38;5;245m"); - } - else - { - // draw in "bright black" (gray) - abandon_line_string.append(L"\x1b[0m" //bright - L"\x1b[30;1m"); //black + } else { + // Draw in "bright black" (gray). + abandon_line_string.append( + L"\x1b[0m" // bright + L"\x1b[30;1m"); // black } abandon_line_string.push_back(omitted_newline_char); - abandon_line_string.append(L"\x1b[0m"); //normal text ANSI escape sequence + abandon_line_string.append(L"\x1b[0m"); // normal text ANSI escape sequence abandon_line_string.append(screen_width - non_space_width, L' '); - } abandon_line_string.push_back(L'\r'); - // now we are certainly on a new line. But we may have dropped the omitted newline char on it. So append enough spaces to overwrite the omitted newline char, and then + // Now we are certainly on a new line. But we may have dropped the omitted newline char on + // it. So append enough spaces to overwrite the omitted newline char, and then clear all the + // spaces from the new line abandon_line_string.append(non_space_width, L' '); abandon_line_string.push_back(L'\r'); - abandon_line_string.append(L"\x1b[2K"); //clear all the spaces from the new line + abandon_line_string.append(L"\x1b[2K"); const std::string narrow_abandon_line_string = wcs2string(abandon_line_string); - write_loop(STDOUT_FILENO, narrow_abandon_line_string.c_str(), narrow_abandon_line_string.size()); + write_loop(STDOUT_FILENO, narrow_abandon_line_string.c_str(), + narrow_abandon_line_string.size()); s->actual.cursor.x = 0; } - if (! abandon_line) - { - /* This should prevent resetting the cursor position during the next repaint. */ + if (!abandon_line) { + // This should prevent resetting the cursor position during the next repaint. write_loop(STDOUT_FILENO, "\r", 1); s->actual.cursor.x = 0; } @@ -1460,15 +1210,12 @@ void s_reset(screen_t *s, screen_reset_mode_t mode) fstat(2, &s->prev_buff_2); } -bool screen_force_clear_to_end() -{ +bool screen_force_clear_to_end() { bool result = false; - if (clr_eos) - { + if (clr_eos) { data_buffer_t output; s_write_mbs(&output, clr_eos); - if (! output.empty()) - { + if (!output.empty()) { write_loop(STDOUT_FILENO, &output.at(0), output.size()); result = true; } @@ -1476,18 +1223,18 @@ bool screen_force_clear_to_end() return result; } -screen_t::screen_t() : - desired(), - actual(), - actual_left_prompt(), - last_right_prompt_width(), - actual_width(SCREEN_WIDTH_UNINITIALIZED), - soft_wrap_location(INVALID_LOCATION), - autosuggestion_is_truncated(false), - need_clear_lines(false), - need_clear_screen(false), - actual_lines_before_reset(0), - prev_buff_1(), prev_buff_2(), post_buff_1(), post_buff_2() -{ -} - +screen_t::screen_t() + : desired(), + actual(), + actual_left_prompt(), + last_right_prompt_width(), + actual_width(SCREEN_WIDTH_UNINITIALIZED), + soft_wrap_location(INVALID_LOCATION), + autosuggestion_is_truncated(false), + need_clear_lines(false), + need_clear_screen(false), + actual_lines_before_reset(0), + prev_buff_1(), + prev_buff_2(), + post_buff_1(), + post_buff_2() {} diff --git a/src/screen.h b/src/screen.h index 9db4d460e..1a26ed130 100644 --- a/src/screen.h +++ b/src/screen.h @@ -1,286 +1,194 @@ -/** \file screen.h High level library for handling the terminal screen - - The screen library allows the interactive reader to write its - output to screen efficiently by keeping an internal representation - of the current screen contents and trying to find a reasonably - efficient way for transforming that to the desired screen content. - - The current implementation is less smart than ncurses allows - and can not for example move blocks of text around to handle text - insertion. - */ +// High level library for handling the terminal screen +// +// The screen library allows the interactive reader to write its output to screen efficiently by +// keeping an internal representation of the current screen contents and trying to find a reasonably +// efficient way for transforming that to the desired screen content. +// +// The current implementation is less smart than ncurses allows and can not for example move blocks +// of text around to handle text insertion. #ifndef FISH_SCREEN_H #define FISH_SCREEN_H #include +#include #include -#include #include +#include +#include #include "common.h" #include "highlight.h" -#include -#include class page_rendering_t; -/** - A class representing a single line of a screen. -*/ -struct line_t -{ +/// A class representing a single line of a screen. +struct line_t { std::vector text; std::vector colors; bool is_soft_wrapped; - line_t() : text(), colors(), is_soft_wrapped(false) - { - } + line_t() : text(), colors(), is_soft_wrapped(false) {} - void clear(void) - { + void clear(void) { text.clear(); colors.clear(); } - void append(wchar_t txt, highlight_spec_t color) - { + void append(wchar_t txt, highlight_spec_t color) { text.push_back(txt); colors.push_back(color); } - void append(const wchar_t *txt, highlight_spec_t color) - { - for (size_t i=0; txt[i]; i++) - { + void append(const wchar_t *txt, highlight_spec_t color) { + for (size_t i = 0; txt[i]; i++) { text.push_back(txt[i]); colors.push_back(color); } } + size_t size(void) const { return text.size(); } + wchar_t char_at(size_t idx) const { return text.at(idx); } - size_t size(void) const - { - return text.size(); - } + highlight_spec_t color_at(size_t idx) const { return colors.at(idx); } - wchar_t char_at(size_t idx) const - { - return text.at(idx); - } - - highlight_spec_t color_at(size_t idx) const - { - return colors.at(idx); - } - - void append_line(const line_t &line) - { + void append_line(const line_t &line) { text.insert(text.end(), line.text.begin(), line.text.end()); colors.insert(colors.end(), line.colors.begin(), line.colors.end()); } - }; -/** - A class representing screen contents. -*/ -class screen_data_t -{ +/// A class representing screen contents. +class screen_data_t { std::vector line_datas; -public: - - struct cursor_t - { + public: + struct cursor_t { int x; int y; - cursor_t() : x(0), y(0) { } - cursor_t(int a, int b) : x(a), y(b) { } + cursor_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) - { - line_datas.resize(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 &insert_line_at_index(size_t idx) - { + line_t &insert_line_at_index(size_t idx) { assert(idx <= line_datas.size()); return *line_datas.insert(line_datas.begin() + idx, line_t()); } - line_t &line(size_t idx) - { - return line_datas.at(idx); - } + line_t &line(size_t idx) { return line_datas.at(idx); } - size_t line_count(void) - { - return line_datas.size(); - } + size_t line_count(void) { return line_datas.size(); } - void append_lines(const screen_data_t &d) - { + void append_lines(const screen_data_t &d) { this->line_datas.insert(this->line_datas.end(), d.line_datas.begin(), d.line_datas.end()); } - bool empty() const - { - return line_datas.empty(); - } + bool empty() const { return line_datas.empty(); } }; -/** - The class representing the current and desired screen contents. -*/ -class screen_t -{ -public: - - /** Constructor */ +/// The class representing the current and desired screen contents. +class screen_t { + public: + /// Constructor screen_t(); - /** - The internal representation of the desired screen contents. - */ + /// The internal representation of the desired screen contents. screen_data_t desired; - /** - The internal representation of the actual screen contents. - */ + /// The internal representation of the actual screen contents. screen_data_t actual; - - /** - A string containing the prompt which was last printed to - the screen. - */ + /// A string containing the prompt which was last printed to the screen. wcstring actual_left_prompt; - - /** Last right prompt width */ + /// Last right prompt width. size_t last_right_prompt_width; - - /** - The actual width of the screen at the time of the last screen - write. - */ + /// The actual width of the screen at the time of the last screen write. int actual_width; - - /** If we support soft wrapping, we can output to this location without any cursor motion. */ + /// If we support soft wrapping, we can output to this location without any cursor motion. screen_data_t::cursor_t soft_wrap_location; - - /** Whether the last-drawn autosuggestion (if any) is truncated, or hidden entirely */ + /// Whether the last-drawn autosuggestion (if any) is truncated, or hidden entirely. bool autosuggestion_is_truncated; - - /** - This flag is set to true when there is reason to suspect that - the parts of the screen lines where the actual content is not - filled in may be non-empty. This means that a clr_eol command - has to be sent to the terminal at the end of each line, including - actual_lines_before_reset. - */ + /// This flag is set to true when there is reason to suspect that the parts of the screen lines + /// where the actual content is not filled in may be non-empty. This means that a clr_eol + /// command has to be sent to the terminal at the end of each line, including + /// actual_lines_before_reset. bool need_clear_lines; - - /** Whether there may be yet more content after the lines, and we issue a clr_eos if possible. */ + /// Whether there may be yet more content after the lines, and we issue a clr_eos if possible. bool need_clear_screen; - - /** 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. */ + /// 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. - */ + /// 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; }; -/** - This is the main function for the screen putput library. It is used - to define the desired contents of the screen. The screen command - will use it's knowlege of the current contents of the screen in - order to render the desired output using as few terminal commands - as possible. +/// This is the main function for the screen putput library. It is used to define the desired +/// contents of the screen. The screen command will use it's knowlege of the current contents of the +/// screen in order to render the desired output using as few terminal commands as possible. +/// +/// \param s the screen on which to write +/// \param left_prompt the prompt to prepend to the command line +/// \param right_prompt the right prompt, or NULL if none +/// \param commandline the command line +/// \param explicit_len the number of characters of the "explicit" (non-autosuggestion) portion of +/// the command line +/// \param colors the colors to use for the comand line +/// \param indent the indent to use for the command line +/// \param cursor_pos where the cursor is +/// \param sel_start_pos where the selections starts (inclusive) +/// \param sel_stop_pos where the selections ends (inclusive) +/// \param pager_data any pager data, to append to the screen +/// \param position_is_within_pager whether the position is within the pager line (first line) +void s_write(screen_t *s, const wcstring &left_prompt, const wcstring &right_prompt, + const wcstring &commandline, size_t explicit_len, const highlight_spec_t *colors, + const int *indent, size_t cursor_pos, size_t sel_start_pos, size_t sel_stop_pos, + const page_rendering_t &pager_data, bool position_is_within_pager); - \param s the screen on which to write - \param left_prompt the prompt to prepend to the command line - \param right_prompt the right prompt, or NULL if none - \param commandline the command line - \param explicit_len the number of characters of the "explicit" (non-autosuggestion) portion of the command line - \param colors the colors to use for the comand line - \param indent the indent to use for the command line - \param cursor_pos where the cursor is - \param sel_start_pos where the selections starts (inclusive) - \param sel_stop_pos where the selections ends (inclusive) - \param pager_data any pager data, to append to the screen - \param position_is_within_pager whether the position is within the pager line (first line) -*/ -void s_write(screen_t *s, - const wcstring &left_prompt, - const wcstring &right_prompt, - const wcstring &commandline, - size_t explicit_len, - const highlight_spec_t *colors, - const int *indent, - size_t cursor_pos, - size_t sel_start_pos, - size_t sel_stop_pos, - const page_rendering_t &pager_data, - bool position_is_within_pager); - -/** - This function resets the screen buffers internal knowledge about - the contents of the screen. Use this function when some other - function than s_write has written to the screen. - - \param s the screen to reset - \param reset_cursor whether the line on which the cursor has changed should be assumed to have changed. If \c reset_cursor is false, the library will attempt to make sure that the screen area does not seem to move up or down on repaint. - \param reset_prompt whether to reset the prompt as well. - - If reset_cursor is incorrectly set to false, this may result in - screen contents being erased. If it is incorrectly set to true, it - may result in one or more lines of garbage on screen on the next - repaint. If this happens during a loop, such as an interactive - resizing, there will be one line of garbage for every repaint, - which will quickly fill the screen. -*/ +/// This function resets the screen buffers internal knowledge about the contents of the screen. Use +/// this function when some other function than s_write has written to the screen. +/// +/// \param s the screen to reset +/// \param reset_cursor whether the line on which the cursor has changed should be assumed to have +/// changed. If \c reset_cursor is false, the library will attempt to make sure that the screen area +/// does not seem to move up or down on repaint. +/// \param reset_prompt whether to reset the prompt as well. +/// +/// If reset_cursor is incorrectly set to false, this may result in screen contents being erased. If +/// it is incorrectly set to true, it may result in one or more lines of garbage on screen on the +/// next repaint. If this happens during a loop, such as an interactive resizing, there will be one +/// line of garbage for every repaint, which will quickly fill the screen. void s_reset(screen_t *s, bool reset_cursor, bool reset_prompt = true); - -enum screen_reset_mode_t -{ - /* Do not make a new line, do not repaint the prompt. */ +enum screen_reset_mode_t { + /// Do not make a new line, do not repaint the prompt. screen_reset_current_line_contents, - - /* Do not make a new line, do repaint the prompt. */ + /// Do not make a new line, do repaint the prompt. screen_reset_current_line_and_prompt, - - /* Abandon the current line, go to the next one, repaint the prompt */ + /// Abandon the current line, go to the next one, repaint the prompt. screen_reset_abandon_line, - - /* Abandon the current line, go to the next one, clear the rest of the screen */ + /// Abandon the current line, go to the next one, clear the rest of the screen. screen_reset_abandon_line_and_clear_to_end_of_screen }; void s_reset(screen_t *s, screen_reset_mode_t mode); -/* Issues an immediate clr_eos, returning if it existed */ +/// Issues an immediate clr_eos, returning if it existed. bool screen_force_clear_to_end(); -/* Returns the length of an escape code. Exposed for testing purposes only. */ +/// Returns the length of an escape code. Exposed for testing purposes only. size_t escape_code_length(const wchar_t *code); #endif From 0aa7fd95b82187e08caba461e7a5f4da7fc908e1 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Tue, 3 May 2016 13:02:26 -0700 Subject: [PATCH 210/363] restyle signal module to match project style Reduces lint errors from 15 to 15 (-0%). Line count from 754 to 438 (-42%). Another step in resolving issue #2902. --- src/signal.cpp | 565 ++++++++++++------------------------------------- src/signal.h | 53 ++--- 2 files changed, 151 insertions(+), 467 deletions(-) diff --git a/src/signal.cpp b/src/signal.cpp index 996d3314b..f5c356f7e 100644 --- a/src/signal.cpp +++ b/src/signal.cpp @@ -1,10 +1,7 @@ -/** \file signal.c - -The library for various signal related issues -*/ -#include -#include +// The library for various signal related issues. #include +#include +#include #ifdef HAVE_SIGINFO_H #include #endif @@ -12,383 +9,167 @@ The library for various signal related issues #include #include "common.h" -#include "fallback.h" // IWYU pragma: keep -#include "wutil.h" // IWYU pragma: keep -#include "signal.h" #include "event.h" -#include "reader.h" +#include "fallback.h" // IWYU pragma: keep #include "proc.h" +#include "reader.h" +#include "signal.h" +#include "wutil.h" // IWYU pragma: keep -/** - Struct describing an entry for the lookup table used to convert - between signal names and signal ids, etc. -*/ -struct lookup_entry -{ - /** - Signal id - */ +/// Struct describing an entry for the lookup table used to convert between signal names and signal +/// ids, etc. +struct lookup_entry { + /// Signal id. int signal; - /** - Signal name - */ + /// Signal name. const wchar_t *name; - /** - Signal description - */ + /// Signal description. const wchar_t *desc; }; -/** - The number of signal blocks in place. Increased by signal_block, decreased by signal_unblock. -*/ -static int block_count=0; +/// The number of signal blocks in place. Increased by signal_block, decreased by signal_unblock. +static int block_count = 0; - -/** - Lookup table used to convert between signal names and signal ids, - etc. -*/ -static const struct lookup_entry lookup[] = -{ +/// Lookup table used to convert between signal names and signal ids, etc. +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. +static int match_signal_name(const wchar_t *canonical, const wchar_t *name) { + if (wcsncasecmp(name, L"sig", 3) == 0) name += 3; -/** - 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) -{ - 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; + wchar_t *end = 0; - for (i=0; lookup[i].desc ; i++) - { - if (match_signal_name(lookup[i].name, str)) - { + for (i = 0; lookup[i].desc; i++) { + if (match_signal_name(lookup[i].name, str)) { return lookup[i].signal; } } - errno=0; + errno = 0; int res = fish_wcstoi(str, &end, 10); - if (!errno && res>=0 && !*end) - return res; + if (!errno && res >= 0 && !*end) return res; return -1; } - -const wchar_t *sig2wcs(int sig) -{ +const wchar_t *sig2wcs(int sig) { int i; - for (i=0; lookup[i].desc ; i++) - { - if (lookup[i].signal == sig) - { + for (i = 0; lookup[i].desc; i++) { + if (lookup[i].signal == sig) { return lookup[i].name; } } @@ -396,14 +177,11 @@ const wchar_t *sig2wcs(int sig) return _(L"Unknown"); } -const wchar_t *signal_get_desc(int sig) -{ +const wchar_t *signal_get_desc(int sig) { int i; - for (i=0; lookup[i].desc ; i++) - { - if (lookup[i].signal == sig) - { + for (i = 0; lookup[i].desc; i++) { + if (lookup[i].signal == sig) { return _(lookup[i].desc); } } @@ -411,103 +189,74 @@ const wchar_t *signal_get_desc(int sig) return _(L"Unknown"); } -/** - Standard signal handler -*/ -static void default_handler(int signal, siginfo_t *info, void *context) -{ - if (event_is_signal_observed(signal)) - { +/// Standard signal handler. +static void default_handler(int signal, siginfo_t *info, void *context) { + if (event_is_signal_observed(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) -{ +/// Respond to a winch signal by checking the terminal size. +static void handle_winch(int sig, siginfo_t *info, void *context) { 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) -{ - if (event_is_signal_observed(SIGHUP)) - { +/// 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) { + if (event_is_signal_observed(SIGHUP)) { default_handler(sig, 0, 0); - } - else - { + } 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) -{ +/// 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) { restore_term_foreground_process_group(); signal(SIGTERM, SIG_DFL); raise(SIGTERM); } -/** - 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) -{ +/// 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) { 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) -{ +/// sigchld handler. Does notification and calls the handler in proc.c. +static void handle_chld(int sig, siginfo_t *info, void *context) { job_handle_signal(sig, info, context); default_handler(sig, info, context); } -void signal_reset_handlers() -{ +void signal_reset_handlers() { int i; struct sigaction act; - sigemptyset(& act.sa_mask); - act.sa_flags=0; - act.sa_handler=SIG_DFL; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + act.sa_handler = SIG_DFL; - for (i=0; lookup[i].desc ; i++) - { + for (i = 0; lookup[i].desc; i++) { sigaction(lookup[i].signal, &act, 0); } } - -/** - Sets appropriate signal handlers. -*/ -void signal_set_handlers() -{ +/// Sets appropriate signal handlers. +void signal_set_handlers() { struct sigaction act; - if (get_is_interactive() == -1) - return; + if (get_is_interactive() == -1) return; - sigemptyset(& act.sa_mask); - act.sa_flags=SA_SIGINFO; + 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 - */ + // 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); @@ -515,19 +264,13 @@ void signal_set_handlers() sigaction(SIGTTOU, &act, 0); sigaction(SIGCHLD, &act, 0); - /* - Ignore sigpipe, which we may get from the universal variable notifier. - */ + // Ignore sigpipe, which we may get from the universal variable notifier. 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; + 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); @@ -537,95 +280,71 @@ void signal_set_handlers() act.sa_sigaction = &handle_int; act.sa_flags = SA_SIGINFO; - if (sigaction(SIGINT, &act, 0)) - { + 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)) - { + 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)) - { + 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)) - { + act.sa_sigaction = &handle_hup; + if (sigaction(SIGHUP, &act, 0)) { wperror(L"sigaction"); FATAL_EXIT(); } - // SIGTERM restores the terminal controlling process before dying + // SIGTERM restores the terminal controlling process before dying. act.sa_flags = SA_SIGINFO; - act.sa_sigaction= &handle_term; - if (sigaction(SIGTERM, &act, 0)) - { + act.sa_sigaction = &handle_term; + if (sigaction(SIGTERM, &act, 0)) { wperror(L"sigaction"); FATAL_EXIT(); } - - } - else - { - /* - Non-interactive. Ignore interrupt, check exit status of - processes to determine result instead. - */ - act.sa_handler=SIG_IGN; - + } else { + // Non-interactive. Ignore interrupt, check exit status of processes to determine result + // instead. + act.sa_handler = SIG_IGN; sigaction(SIGINT, &act, 0); sigaction(SIGQUIT, &act, 0); - act.sa_handler=SIG_DFL; - + act.sa_handler = SIG_DFL; act.sa_sigaction = &handle_chld; act.sa_flags = SA_SIGINFO; - if (sigaction(SIGCHLD, &act, 0)) - { + 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)) + // 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) - { + if (do_handle) { act.sa_flags = SA_SIGINFO; act.sa_sigaction = &default_handler; - } - else - { + } else { act.sa_flags = 0; act.sa_handler = SIG_DFL; } @@ -633,57 +352,45 @@ void signal_handle(int sig, int do_handle) sigaction(sig, &act, 0); } -void get_signals_with_handlers(sigset_t *set) -{ +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); - if (act.sa_handler != SIG_DFL) - sigaddset(set, lookup[i].signal); + if (act.sa_handler != SIG_DFL) sigaddset(set, lookup[i].signal); } } -void signal_block() -{ +void signal_block() { ASSERT_IS_MAIN_THREAD(); sigset_t chldset; - if (!block_count) - { + 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 ); + // debug( 0, L"signal block level increased to %d", block_count ); } -void signal_unblock() -{ +void signal_unblock() { ASSERT_IS_MAIN_THREAD(); sigset_t chldset; block_count--; - if (block_count < 0) - { + if (block_count < 0) { debug(0, _(L"Signal block mismatch")); bugreport(); FATAL_EXIT(); } - if (!block_count) - { + 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 ); -} - -bool signal_is_blocked() -{ - return !!block_count; + // debug( 0, L"signal block level decreased to %d", block_count ); } +bool signal_is_blocked() { return !!block_count; } diff --git a/src/signal.h b/src/signal.h index 4a22520a8..fa24cc2b2 100644 --- a/src/signal.h +++ b/src/signal.h @@ -1,65 +1,42 @@ -/** \file signal.h - -The library for various signal related issues -*/ +// The library for various signal related issues. #ifndef FISH_SIGNALH #define FISH_SIGNALH #include #include -/** - Get the integer signal value representing the specified signal, or - -1 of no signal was found -*/ +/// Get the integer signal value representing the specified signal, or -1 of no signal was found. int wcs2sig(const wchar_t *str); -/** - Get string representation of a signal -*/ +/// Get string representation of a signal. const wchar_t *sig2wcs(int sig); -/** - Returns a description of the specified signal. -*/ +/// Returns a description of the specified signal. const wchar_t *signal_get_desc(int sig); -/** - Set all signal handlers to SIG_DFL -*/ +/// Set all signal handlers to SIG_DFL. void signal_reset_handlers(); -/** - Set signal handlers to fish default handlers -*/ +/// Set signal handlers to fish default handlers. void signal_set_handlers(); -/** - Tell fish what to do on the specified signal. - - \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 -*/ +/// Tell fish what to do on the specified signal. +/// +/// \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); -/** - Block all signals -*/ +/// Block all signals. void signal_block(); -/** - Unblock all signals -*/ +/// Unblock all signals. void signal_unblock(); -/** - Returns true if signals are being blocked -*/ +/// Returns true if signals are being blocked. bool signal_is_blocked(); -/** - Returns signals with non-default handlers -*/ +/// Returns signals with non-default handlers. void get_signals_with_handlers(sigset_t *set); #endif From c14bac42846506ab01090e1f6384a60126d7c596 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Tue, 3 May 2016 14:35:12 -0700 Subject: [PATCH 211/363] restyle tokenizer module to match project style Reduces lint errors from 70 to 46 (-34%). Line count from 1158 to 936 (-19%). Another step in resolving issue #2902. --- src/tokenizer.cpp | 906 +++++++++++++++++++--------------------------- src/tokenizer.h | 206 +++++------ 2 files changed, 445 insertions(+), 667 deletions(-) diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 2eca66276..37263fe23 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -1,71 +1,64 @@ -/** \file tokenizer.c - -A specialized tokenizer for tokenizing the fish language. In the -future, the tokenizer should be extended to support marks, -tokenizing multiple strings and disposing of unused string -segments. -*/ +// A specialized tokenizer for tokenizing the fish language. In the future, the tokenizer should be +// extended to support marks, tokenizing multiple strings and disposing of unused string segments. +#include +#include +#include +#include #include #include -#include -#include -#include #include -#include -#include "fallback.h" // IWYU pragma: keep #include "common.h" -#include "wutil.h" // IWYU pragma: keep +#include "fallback.h" // IWYU pragma: keep #include "tokenizer.h" +#include "wutil.h" // IWYU pragma: keep -/* Wow what a hack */ -#define TOK_CALL_ERROR(t, e, x, where) do { (t)->call_error((e), where, (t)->squash_errors ? L"" : (x)); } while (0) +// Wow what a hack. +#define TOK_CALL_ERROR(t, e, x, where) \ + do { \ + (t)->call_error((e), where, (t)->squash_errors ? L"" : (x)); \ + } while (0) -/** - Error string for unexpected end of string -*/ -#define QUOTE_ERROR _( L"Unexpected end of string, quotes are not balanced" ) +/// Error string for unexpected end of string. +#define QUOTE_ERROR _(L"Unexpected end of string, quotes are not balanced") -/** - Error string for mismatched parenthesis -*/ -#define PARAN_ERROR _( L"Unexpected end of string, parenthesis do not match" ) +/// Error string for mismatched parenthesis. +#define PARAN_ERROR _(L"Unexpected end of string, parenthesis do not match") -/** - Error string for mismatched square brackets -*/ -#define SQUARE_BRACKET_ERROR _( L"Unexpected end of string, square brackets do not match" ) +/// Error string for mismatched square brackets. +#define SQUARE_BRACKET_ERROR _(L"Unexpected end of string, square brackets do not match") -/** - Error string for unterminated escape (backslash without continuation) - */ -#define UNTERMINATED_ESCAPE_ERROR _( L"Unexpected end of string, incomplete escape sequence" ) +/// Error string for unterminated escape (backslash without continuation). +#define UNTERMINATED_ESCAPE_ERROR _(L"Unexpected end of string, incomplete escape sequence") +/// Error string for invalid redirections. +#define REDIRECT_ERROR _(L"Invalid input/output redirection") +/// Error string for when trying to pipe from fd 0. +#define PIPE_ERROR _(L"Cannot use stdin (fd 0) as pipe output") -/** - Error string for invalid redirections -*/ -#define REDIRECT_ERROR _( L"Invalid input/output redirection" ) - -/** - Error string for when trying to pipe from fd 0 -*/ -#define PIPE_ERROR _( L"Cannot use stdin (fd 0) as pipe output" ) - -/** - Set the latest tokens string to be the specified error message -*/ -void tokenizer_t::call_error(enum tokenizer_error error_type, const wchar_t *where, const wchar_t *error_message) -{ +/// Set the latest tokens string to be the specified error message. +void tokenizer_t::call_error(enum tokenizer_error error_type, const wchar_t *where, + const wchar_t *error_message) { this->last_type = TOK_ERROR; this->error = error_type; this->global_error_offset = where ? where - this->orig_buff : 0; this->last_token = error_message; } -tokenizer_t::tokenizer_t(const wchar_t *b, tok_flags_t flags) : buff(b), orig_buff(b), last_type(TOK_NONE), last_pos(0), has_next(false), accept_unfinished(false), show_comments(false), show_blank_lines(false), error(TOK_ERROR_NONE), global_error_offset(-1), squash_errors(false), continue_line_after_comment(false) -{ +tokenizer_t::tokenizer_t(const wchar_t *b, tok_flags_t flags) + : buff(b), + orig_buff(b), + last_type(TOK_NONE), + last_pos(0), + has_next(false), + accept_unfinished(false), + show_comments(false), + show_blank_lines(false), + error(TOK_ERROR_NONE), + global_error_offset(-1), + squash_errors(false), + continue_line_after_comment(false) { assert(b != NULL); this->accept_unfinished = !!(flags & TOK_ACCEPT_UNFINISHED); @@ -77,48 +70,46 @@ tokenizer_t::tokenizer_t(const wchar_t *b, tok_flags_t flags) : buff(b), orig_bu this->tok_next(); } -bool tokenizer_t::next(struct tok_t *result) -{ +bool tokenizer_t::next(struct tok_t *result) { assert(result != NULL); - if (! this->has_next) - { + if (!this->has_next) { return false; } - + const size_t current_pos = this->buff - this->orig_buff; - - /* We want to copy our last_token into result->text. If we just do this naively via =, we are liable to trigger std::string's CoW implementation: result->text's storage will be deallocated and instead will acquire a reference to last_token's storage. But last_token will be overwritten soon, which will trigger a new allocation and a copy. So our attempt to re-use result->text's storage will have failed. To ensure that doesn't happen, use assign() with wchar_t */ + + // We want to copy our last_token into result->text. If we just do this naively via =, we are + // liable to trigger std::string's CoW implementation: result->text's storage will be + // deallocated and instead will acquire a reference to last_token's storage. But last_token will + // be overwritten soon, which will trigger a new allocation and a copy. So our attempt to re-use + // result->text's storage will have failed. To ensure that doesn't happen, use assign() with + // wchar_t. result->text.assign(this->last_token.data(), this->last_token.size()); - + result->type = this->last_type; result->offset = this->last_pos; result->error = this->last_type == TOK_ERROR ? this->error : TOK_ERROR_NONE; assert(this->buff >= this->orig_buff); - - /* Compute error offset */ + + // Compute error offset. result->error_offset = 0; - if (this->last_type == TOK_ERROR && this->global_error_offset >= this->last_pos && this->global_error_offset < current_pos) - { + if (this->last_type == TOK_ERROR && this->global_error_offset >= this->last_pos && + this->global_error_offset < current_pos) { result->error_offset = this->global_error_offset - this->last_pos; } - + assert(this->buff >= this->orig_buff); result->length = current_pos >= this->last_pos ? current_pos - this->last_pos : 0; - + this->tok_next(); return true; } -/** - Tests if this character can be a part of a string. The redirect ^ is allowed unless it's the first character. - Hash (#) starts a comment if it's the first character in a token; otherwise it is considered a string character. - See #953. -*/ -static bool tok_is_string_character(wchar_t c, bool is_first) -{ - switch (c) - { - /* Unconditional separators */ +/// Tests if this character can be a part of a string. The redirect ^ is allowed unless it's the +/// first character. Hash (#) starts a comment if it's the first character in a token; otherwise it +/// is considered a string character. See issue #953. +static bool tok_is_string_character(wchar_t c, bool is_first) { + switch (c) { case L'\0': case L' ': case L'\n': @@ -128,74 +119,57 @@ static bool tok_is_string_character(wchar_t c, bool is_first) case L'\r': case L'<': case L'>': - case L'&': + case L'&': { + // Unconditional separators. return false; - - /* Conditional separator */ - case L'^': - return ! is_first; - - default: - return true; + } + case L'^': { + // Conditional separator. + return !is_first; + } + default: { return true; } } } -/** - Quick test to catch the most common 'non-magical' characters, makes - read_string slightly faster by adding a fast path for the most - common characters. This is obviously not a suitable replacement for - iswalpha. -*/ -static int myal(wchar_t c) -{ - return (c>=L'a' && c<=L'z') || (c>=L'A'&&c<=L'Z'); -} +/// Quick test to catch the most common 'non-magical' characters, makes read_string slightly faster +/// by adding a fast path for the most common characters. This is obviously not a suitable +/// replacement for iswalpha. +static int myal(wchar_t c) { return (c >= L'a' && c <= L'z') || (c >= L'A' && c <= L'Z'); } -/** - Read the next token as a string -*/ -void tokenizer_t::read_string() -{ +/// Read the next token as a string. +void tokenizer_t::read_string() { long len; - int do_loop=1; - size_t paran_count=0; - - // up to 96 open parens, before we give up on good error reporting + int do_loop = 1; + size_t paran_count = 0; + // Up to 96 open parens, before we give up on good error reporting. const size_t paran_offsets_max = 96; size_t paran_offsets[paran_offsets_max]; - - // where the open bracket is + // Where the open bracket is. size_t offset_of_bracket = 0; - - const wchar_t * const start = this->buff; + const wchar_t *const start = this->buff; bool is_first = true; - enum tok_mode_t - { - mode_regular_text = 0, // regular text - mode_subshell = 1, // inside of subshell - mode_array_brackets = 2, // inside of array brackets - mode_array_brackets_and_subshell = 3 // inside of array brackets and subshell, like in '$foo[(ech' + enum tok_mode_t { + mode_regular_text = 0, // regular text + mode_subshell = 1, // inside of subshell + mode_array_brackets = 2, // inside of array brackets + mode_array_brackets_and_subshell = + 3 // inside of array brackets and subshell, like in '$foo[(ech' } mode = mode_regular_text; - while (1) - { - if (!myal(*this->buff)) - { - if (*this->buff == L'\\') - { + while (1) { + if (!myal(*this->buff)) { + if (*this->buff == L'\\') { const wchar_t *error_location = this->buff; this->buff++; - if (*this->buff == L'\0') - { - if ((!this->accept_unfinished)) - { - TOK_CALL_ERROR(this, TOK_UNTERMINATED_ESCAPE, UNTERMINATED_ESCAPE_ERROR, error_location); + if (*this->buff == L'\0') { + if ((!this->accept_unfinished)) { + TOK_CALL_ERROR(this, TOK_UNTERMINATED_ESCAPE, UNTERMINATED_ESCAPE_ERROR, + error_location); return; - } - else - { - /* Since we are about to increment tok->buff, decrement it first so the increment doesn't go past the end of the buffer. https://github.com/fish-shell/fish-shell/issues/389 */ + } else { + // Since we are about to increment tok->buff, decrement it first so the + // increment doesn't go past the end of the buffer. See issue #389. this->buff--; do_loop = 0; } @@ -205,60 +179,43 @@ void tokenizer_t::read_string() continue; } - switch (mode) - { - case mode_regular_text: - { - switch (*this->buff) - { - case L'(': - { - paran_count=1; + switch (mode) { + case mode_regular_text: { + switch (*this->buff) { + case L'(': { + paran_count = 1; paran_offsets[0] = this->buff - this->orig_buff; mode = mode_subshell; break; } - - case L'[': - { - if (this->buff != start) - { + case L'[': { + if (this->buff != start) { mode = mode_array_brackets; offset_of_bracket = this->buff - this->orig_buff; } break; } - case L'\'': - case L'"': - { - + case L'"': { const wchar_t *end = quote_end(this->buff); - if (end) - { - this->buff=end; - } - else - { + if (end) { + this->buff = end; + } else { const wchar_t *error_loc = this->buff; this->buff += wcslen(this->buff); - if (! this->accept_unfinished) - { - TOK_CALL_ERROR(this, TOK_UNTERMINATED_QUOTE, QUOTE_ERROR, error_loc); + if (!this->accept_unfinished) { + TOK_CALL_ERROR(this, TOK_UNTERMINATED_QUOTE, QUOTE_ERROR, + error_loc); return; } do_loop = 0; - } break; } - - default: - { - if (! tok_is_string_character(*(this->buff), is_first)) - { - do_loop=0; + default: { + if (!tok_is_string_character(*(this->buff), is_first)) { + do_loop = 0; } } } @@ -266,352 +223,291 @@ void tokenizer_t::read_string() } case mode_array_brackets_and_subshell: - case mode_subshell: - { - switch (*this->buff) - { + case mode_subshell: { + switch (*this->buff) { case L'\'': - case L'\"': - { + case L'\"': { const wchar_t *end = quote_end(this->buff); - if (end) - { + if (end) { this->buff = end; - } - else - { + } else { const wchar_t *error_loc = this->buff; this->buff += wcslen(this->buff); - if ((!this->accept_unfinished)) - { - TOK_CALL_ERROR(this, TOK_UNTERMINATED_QUOTE, QUOTE_ERROR, error_loc); + if ((!this->accept_unfinished)) { + TOK_CALL_ERROR(this, TOK_UNTERMINATED_QUOTE, QUOTE_ERROR, + error_loc); return; } do_loop = 0; } - break; } - - case L'(': - if (paran_count < paran_offsets_max) - { + case L'(': { + if (paran_count < paran_offsets_max) { paran_offsets[paran_count] = this->buff - this->orig_buff; } paran_count++; break; - case L')': + } + case L')': { assert(paran_count > 0); paran_count--; - if (paran_count == 0) - { - mode = (mode == mode_array_brackets_and_subshell ? mode_array_brackets : mode_regular_text); + if (paran_count == 0) { + mode = + (mode == mode_array_brackets_and_subshell ? mode_array_brackets + : mode_regular_text); } break; - case L'\0': + } + case L'\0': { do_loop = 0; break; + } } break; } - case mode_array_brackets: - { - switch (*this->buff) - { - case L'(': - paran_count=1; + case mode_array_brackets: { + switch (*this->buff) { + case L'(': { + paran_count = 1; paran_offsets[0] = this->buff - this->orig_buff; mode = mode_array_brackets_and_subshell; break; - - case L']': + } + case L']': { mode = mode_regular_text; break; - - case L'\0': + } + case L'\0': { do_loop = 0; break; + } } break; } } } - - if (!do_loop) - break; + if (!do_loop) break; this->buff++; is_first = false; } - if ((!this->accept_unfinished) && (mode != mode_regular_text)) - { - switch (mode) - { - case mode_subshell: - { - // Determine the innermost opening paran offset by interrogating paran_offsets + if ((!this->accept_unfinished) && (mode != mode_regular_text)) { + switch (mode) { + case mode_subshell: { + // Determine the innermost opening paran offset by interrogating paran_offsets. assert(paran_count > 0); size_t offset_of_open_paran = 0; - if (paran_count <= paran_offsets_max) - { + if (paran_count <= paran_offsets_max) { offset_of_open_paran = paran_offsets[paran_count - 1]; } - - TOK_CALL_ERROR(this, TOK_UNTERMINATED_SUBSHELL, PARAN_ERROR, this->orig_buff + offset_of_open_paran); + + TOK_CALL_ERROR(this, TOK_UNTERMINATED_SUBSHELL, PARAN_ERROR, + this->orig_buff + offset_of_open_paran); break; } - case mode_array_brackets: - case mode_array_brackets_and_subshell: - { - TOK_CALL_ERROR(this, TOK_UNTERMINATED_SLICE, SQUARE_BRACKET_ERROR, this->orig_buff + offset_of_bracket); + case mode_array_brackets_and_subshell: { + TOK_CALL_ERROR(this, TOK_UNTERMINATED_SLICE, SQUARE_BRACKET_ERROR, + this->orig_buff + offset_of_bracket); break; } - - default: + default: { assert(0 && "Unexpected mode in read_string"); break; + } } return; } - len = this->buff - start; this->last_token.assign(start, len); this->last_type = TOK_STRING; } -/** - Read the next token as a comment. -*/ -void tokenizer_t::read_comment() -{ +/// Read the next token as a comment. +void tokenizer_t::read_comment() { const wchar_t *start = this->buff; - while (*(this->buff)!= L'\n' && *(this->buff)!= L'\0') - this->buff++; + while (*(this->buff) != L'\n' && *(this->buff) != L'\0') this->buff++; size_t len = this->buff - start; this->last_token.assign(start, len); this->last_type = TOK_COMMENT; } - - -/* Reads a redirection or an "fd pipe" (like 2>|) from a string. Returns how many characters were consumed. If zero, then this string was not a redirection. - - Also returns by reference the redirection mode, and the fd to redirection. If there is overflow, *out_fd is set to -1. -*/ -static size_t read_redirection_or_fd_pipe(const wchar_t *buff, enum token_type *out_redirection_mode, int *out_fd) -{ +/// Reads a redirection or an "fd pipe" (like 2>|) from a string. Returns how many characters were +/// consumed. If zero, then this string was not a redirection. Also returns by reference the +/// redirection mode, and the fd to redirection. If there is overflow, *out_fd is set to -1. +static size_t read_redirection_or_fd_pipe(const wchar_t *buff, + enum token_type *out_redirection_mode, int *out_fd) { bool errored = false; int fd = 0; enum token_type redirection_mode = TOK_NONE; size_t idx = 0; - /* Determine the fd. This may be specified as a prefix like '2>...' or it may be implicit like '>' or '^'. Try parsing out a number; if we did not get any digits then infer it from the first character. Watch out for overflow. */ + // Determine the fd. This may be specified as a prefix like '2>...' or it may be implicit like + // '>' or '^'. Try parsing out a number; if we did not get any digits then infer it from the + // first character. Watch out for overflow. long long big_fd = 0; - for (; iswdigit(buff[idx]); idx++) - { - /* Note that it's important we consume all the digits here, even if it overflows. */ - if (big_fd <= INT_MAX) - big_fd = big_fd * 10 + (buff[idx] - L'0'); + for (; iswdigit(buff[idx]); idx++) { + // Note that it's important we consume all the digits here, even if it overflows. + if (big_fd <= INT_MAX) big_fd = big_fd * 10 + (buff[idx] - L'0'); } fd = (big_fd > INT_MAX ? -1 : static_cast(big_fd)); - if (idx == 0) - { - /* We did not find a leading digit, so there's no explicit fd. Infer it from the type */ - switch (buff[idx]) - { - case L'>': + if (idx == 0) { + // We did not find a leading digit, so there's no explicit fd. Infer it from the type. + switch (buff[idx]) { + case L'>': { fd = STDOUT_FILENO; break; - case L'<': + } + case L'<': { fd = STDIN_FILENO; break; - case L'^': + } + case L'^': { fd = STDERR_FILENO; break; - default: + } + default: { errored = true; break; + } } } - /* Either way we should have ended on the redirection character itself like '>' */ - wchar_t redirect_char = buff[idx++]; //note increment of idx - if (redirect_char == L'>' || redirect_char == L'^') - { + // Either way we should have ended on the redirection character itself like '>'. + wchar_t redirect_char = buff[idx++]; // note increment of idx + if (redirect_char == L'>' || redirect_char == L'^') { redirection_mode = TOK_REDIRECT_OUT; - if (buff[idx] == redirect_char) - { - /* Doubled up like ^^ or >>. That means append */ + if (buff[idx] == redirect_char) { + // Doubled up like ^^ or >>. That means append. redirection_mode = TOK_REDIRECT_APPEND; idx++; } - } - else if (redirect_char == L'<') - { + } else if (redirect_char == L'<') { redirection_mode = TOK_REDIRECT_IN; - } - else - { - /* Something else */ + } else { + // Something else. errored = true; } - /* Don't return valid-looking stuff on error */ - if (errored) - { + // Don't return valid-looking stuff on error. + if (errored) { idx = 0; redirection_mode = TOK_NONE; - } - else - { - /* Optional characters like & or ?, or the pipe char | */ + } else { + // Optional characters like & or ?, or the pipe char |. wchar_t opt_char = buff[idx]; - if (opt_char == L'&') - { + if (opt_char == L'&') { redirection_mode = TOK_REDIRECT_FD; idx++; - } - else if (opt_char == L'?') - { + } else if (opt_char == L'?') { redirection_mode = TOK_REDIRECT_NOCLOB; idx++; - } - else if (opt_char == L'|') - { - /* So the string looked like '2>|'. This is not a redirection - it's a pipe! That gets handled elsewhere. */ + } else if (opt_char == L'|') { + // So the string looked like '2>|'. This is not a redirection - it's a pipe! That gets + // handled elsewhere. redirection_mode = TOK_PIPE; idx++; } } - /* Return stuff */ - if (out_redirection_mode != NULL) - *out_redirection_mode = redirection_mode; - if (out_fd != NULL) - *out_fd = fd; + // Return stuff. + if (out_redirection_mode != NULL) *out_redirection_mode = redirection_mode; + if (out_fd != NULL) *out_fd = fd; return idx; } -enum token_type redirection_type_for_string(const wcstring &str, int *out_fd) -{ +enum token_type redirection_type_for_string(const wcstring &str, int *out_fd) { enum token_type mode = TOK_NONE; int fd = 0; read_redirection_or_fd_pipe(str.c_str(), &mode, &fd); - /* Redirections only, no pipes */ - if (mode == TOK_PIPE || fd < 0) - mode = TOK_NONE; - if (out_fd != NULL) - *out_fd = fd; + // Redirections only, no pipes. + if (mode == TOK_PIPE || fd < 0) mode = TOK_NONE; + if (out_fd != NULL) *out_fd = fd; return mode; } -int fd_redirected_by_pipe(const wcstring &str) -{ - /* Hack for the common case */ - if (str == L"|") - { +int fd_redirected_by_pipe(const wcstring &str) { + // Hack for the common case. + if (str == L"|") { return STDOUT_FILENO; } enum token_type mode = TOK_NONE; int fd = 0; read_redirection_or_fd_pipe(str.c_str(), &mode, &fd); - /* Pipes only */ - if (mode != TOK_PIPE || fd < 0) - fd = -1; + // Pipes only. + if (mode != TOK_PIPE || fd < 0) fd = -1; return fd; } -int oflags_for_redirection_type(enum token_type type) -{ - switch (type) - { - case TOK_REDIRECT_APPEND: +int oflags_for_redirection_type(enum token_type type) { + switch (type) { + case TOK_REDIRECT_APPEND: { return O_CREAT | O_APPEND | O_WRONLY; - case TOK_REDIRECT_OUT: + } + case TOK_REDIRECT_OUT: { return O_CREAT | O_WRONLY | O_TRUNC; - case TOK_REDIRECT_NOCLOB: + } + case TOK_REDIRECT_NOCLOB: { return O_CREAT | O_EXCL | O_WRONLY; - case TOK_REDIRECT_IN: + } + case TOK_REDIRECT_IN: { return O_RDONLY; - - default: - return -1; + } + default: { return -1; } } } -/** - Test if a character is whitespace. Differs from iswspace in that it - does not consider a newline to be whitespace. -*/ -static bool my_iswspace(wchar_t c) -{ - return c != L'\n' && iswspace(c); -} +/// Test if a character is whitespace. Differs from iswspace in that it does not consider a newline +/// to be whitespace. +static bool my_iswspace(wchar_t c) { return c != L'\n' && iswspace(c); } -void tokenizer_t::tok_next() -{ - if (this->last_type == TOK_ERROR) - { - this->has_next=false; +void tokenizer_t::tok_next() { + if (this->last_type == TOK_ERROR) { + this->has_next = false; return; } - if (!this->has_next) - { - /* wprintf( L"EOL\n" );*/ + if (!this->has_next) { + // wprintf( L"EOL\n" ); this->last_type = TOK_END; return; } - while (1) - { - if (this->buff[0] == L'\\' && this->buff[1] == L'\n') - { + while (1) { + if (this->buff[0] == L'\\' && this->buff[1] == L'\n') { this->buff += 2; this->continue_line_after_comment = true; - } - else if (my_iswspace(this->buff[0])) - { + } else if (my_iswspace(this->buff[0])) { this->buff++; - } - else - { + } else { break; } } - - while (*this->buff == L'#') - { - if (this->show_comments) - { + while (*this->buff == L'#') { + if (this->show_comments) { this->last_pos = this->buff - this->orig_buff; this->read_comment(); - if (this->buff[0] == L'\n' && this->continue_line_after_comment) - this->buff++; + if (this->buff[0] == L'\n' && this->continue_line_after_comment) this->buff++; return; - } - else - { - while (*(this->buff)!= L'\n' && *(this->buff)!= L'\0') - this->buff++; + } else { + while (*(this->buff) != L'\n' && *(this->buff) != L'\0') this->buff++; - if (this->buff[0] == L'\n' && this->continue_line_after_comment) - this->buff++; + if (this->buff[0] == L'\n' && this->continue_line_after_comment) this->buff++; } while (my_iswspace(*(this->buff))) { @@ -623,173 +519,141 @@ void tokenizer_t::tok_next() this->last_pos = this->buff - this->orig_buff; - switch (*this->buff) - { - case L'\0': + switch (*this->buff) { + case L'\0': { this->last_type = TOK_END; - /*fwprintf( stderr, L"End of string\n" );*/ + // fwprintf( stderr, L"End of string\n" ); this->has_next = false; break; - case L'\r': // carriage-return - case L'\n': // newline - case L';': + } + case L'\r': // carriage-return + case L'\n': // newline + case L';': { this->last_type = TOK_END; this->buff++; - // Hack: when we get a newline, swallow as many as we can - // This compresses multiple subsequent newlines into a single one - if (! this->show_blank_lines) - { - while (*this->buff == L'\n' || *this->buff == 13 /* CR */ || *this->buff == ' ' || *this->buff == '\t') - { + // Hack: when we get a newline, swallow as many as we can. This compresses multiple + // subsequent newlines into a single one. + if (!this->show_blank_lines) { + while (*this->buff == L'\n' || *this->buff == 13 /* CR */ || *this->buff == ' ' || + *this->buff == '\t') { this->buff++; } } this->last_token.clear(); break; - case L'&': + } + case L'&': { this->last_type = TOK_BACKGROUND; this->buff++; break; - - case L'|': + } + case L'|': { this->last_token = L"1"; this->last_type = TOK_PIPE; this->buff++; break; - + } case L'>': case L'<': - case L'^': - { - /* There's some duplication with the code in the default case below. The key difference here is that we must never parse these as a string; a failed redirection is an error! */ + case L'^': { + // There's some duplication with the code in the default case below. The key difference + // here is that we must never parse these as a string; a failed redirection is an error! enum token_type mode = TOK_NONE; int fd = -1; size_t consumed = read_redirection_or_fd_pipe(this->buff, &mode, &fd); - if (consumed == 0 || fd < 0) - { + if (consumed == 0 || fd < 0) { TOK_CALL_ERROR(this, TOK_OTHER, REDIRECT_ERROR, this->buff); - } - else - { + } else { this->buff += consumed; this->last_type = mode; this->last_token = to_string(fd); } + break; } - break; - - default: - { - /* Maybe a redirection like '2>&1', maybe a pipe like 2>|, maybe just a string */ + default: { + // Maybe a redirection like '2>&1', maybe a pipe like 2>|, maybe just a string. const wchar_t *error_location = this->buff; size_t consumed = 0; enum token_type mode = TOK_NONE; int fd = -1; - if (iswdigit(*this->buff)) - { + if (iswdigit(*this->buff)) { consumed = read_redirection_or_fd_pipe(this->buff, &mode, &fd); } - if (consumed > 0) - { - /* It looks like a redirection or a pipe. But we don't support piping fd 0. Note that fd 0 may be -1, indicating overflow; but we don't treat that as a tokenizer error. */ - if (mode == TOK_PIPE && fd == 0) - { + if (consumed > 0) { + // It looks like a redirection or a pipe. But we don't support piping fd 0. Note + // that fd 0 may be -1, indicating overflow; but we don't treat that as a tokenizer + // error. + if (mode == TOK_PIPE && fd == 0) { TOK_CALL_ERROR(this, TOK_OTHER, PIPE_ERROR, error_location); - } - else - { + } else { this->buff += consumed; this->last_type = mode; this->last_token = to_string(fd); } - } - else - { - /* Not a redirection or pipe, so just a string */ + } else { + // Not a redirection or pipe, so just a string. this->read_string(); } + break; } - break; } - } -wcstring tok_first(const wcstring &str) -{ +wcstring tok_first(const wcstring &str) { wcstring result; tokenizer_t t(str.c_str(), TOK_SQUASH_ERRORS); tok_t token; - if (t.next(&token) && token.type == TOK_STRING) - { + if (t.next(&token) && token.type == TOK_STRING) { result.swap(token.text); } return result; } -bool move_word_state_machine_t::consume_char_punctuation(wchar_t c) -{ - enum - { - s_always_one = 0, - s_whitespace, - s_alphanumeric, - s_end - }; +bool move_word_state_machine_t::consume_char_punctuation(wchar_t c) { + enum { s_always_one = 0, s_whitespace, s_alphanumeric, s_end }; bool consumed = false; - while (state != s_end && ! consumed) - { - switch (state) - { - case s_always_one: - /* Always consume the first character */ + while (state != s_end && !consumed) { + switch (state) { + case s_always_one: { + // Always consume the first character. consumed = true; state = s_whitespace; break; - - case s_whitespace: - if (iswspace(c)) - { - /* Consumed whitespace */ + } + case s_whitespace: { + if (iswspace(c)) { + // Consumed whitespace. consumed = true; - } - else - { + } else { state = s_alphanumeric; } break; - - case s_alphanumeric: - if (iswalnum(c)) - { - /* Consumed alphanumeric */ - consumed = true; - } - else - { + } + case s_alphanumeric: { + if (iswalnum(c)) { + consumed = true; // consumed alphanumeric + } else { state = s_end; } break; - + } case s_end: - default: - break; + default: { break; } } } return consumed; } -bool move_word_state_machine_t::is_path_component_character(wchar_t c) -{ - /* Always treat separators as first. All this does is ensure that we treat ^ as a string character instead of as stderr redirection, which I hypothesize is usually what is desired. */ - return tok_is_string_character(c, true) && ! wcschr(L"/={,}'\"", c); +bool move_word_state_machine_t::is_path_component_character(wchar_t c) { + // Always treat separators as first. All this does is ensure that we treat ^ as a string + // character instead of as stderr redirection, which I hypothesize is usually what is desired. + return tok_is_string_character(c, true) && !wcschr(L"/={,}'\"", c); } -bool move_word_state_machine_t::consume_char_path_components(wchar_t c) -{ - enum - { +bool move_word_state_machine_t::consume_char_path_components(wchar_t c) { + enum { s_initial_punctuation, s_whitespace, s_separator, @@ -798,156 +662,112 @@ bool move_word_state_machine_t::consume_char_path_components(wchar_t c) s_end }; - //printf("state %d, consume '%lc'\n", state, c); + // printf("state %d, consume '%lc'\n", state, c); bool consumed = false; - while (state != s_end && ! consumed) - { - switch (state) - { - case s_initial_punctuation: - if (! is_path_component_character(c)) - { + while (state != s_end && !consumed) { + switch (state) { + case s_initial_punctuation: { + if (!is_path_component_character(c)) { consumed = true; } state = s_whitespace; break; - - case s_whitespace: - if (iswspace(c)) - { - /* Consumed whitespace */ - consumed = true; - } - else if (c == L'/' || is_path_component_character(c)) - { - /* Path component */ - state = s_slash; - } - else - { - /* Path separator */ - state = s_separator; + } + case s_whitespace: { + if (iswspace(c)) { + consumed = true; // consumed whitespace + } else if (c == L'/' || is_path_component_character(c)) { + state = s_slash; // path component + } else { + state = s_separator; // path separator } break; - - case s_separator: - if (! iswspace(c) && ! is_path_component_character(c)) - { - /* Consumed separator */ - consumed = true; + } + case s_separator: { + if (!iswspace(c) && !is_path_component_character(c)) { + consumed = true; // consumed separator } - else - { + else { state = s_end; } break; - - case s_slash: - if (c == L'/') - { - /* Consumed slash */ - consumed = true; - } - else - { + } + case s_slash: { + if (c == L'/') { + consumed = true; // consumed slash + } else { state = s_path_component_characters; } break; - - case s_path_component_characters: - if (is_path_component_character(c)) - { - /* Consumed string character except slash */ - consumed = true; - } - else - { + } + case s_path_component_characters: { + if (is_path_component_character(c)) { + consumed = true; // consumed string character except slash + } else { state = s_end; } break; - - /* We won't get here, but keep the compiler happy */ + } case s_end: - default: + default: { + // We won't get here, but keep the compiler happy. break; + } } } return consumed; } -bool move_word_state_machine_t::consume_char_whitespace(wchar_t c) -{ - enum - { - s_always_one = 0, - s_blank, - s_graph, - s_end - }; +bool move_word_state_machine_t::consume_char_whitespace(wchar_t c) { + enum { s_always_one = 0, s_blank, s_graph, s_end }; bool consumed = false; - while (state != s_end && ! consumed) - { - switch (state) - { - case s_always_one: - /* Always consume the first character */ - consumed = true; + while (state != s_end && !consumed) { + switch (state) { + case s_always_one: { + consumed = true; // always consume the first character state = s_blank; break; - - case s_blank: - if (iswblank(c)) - { - /* Consumed whitespace */ - consumed = true; - } - else - { + } + case s_blank: { + if (iswblank(c)) { + consumed = true; // consumed whitespace + } else { state = s_graph; } break; - - case s_graph: - if (iswgraph(c)) - { - /* Consumed printable non-space */ - consumed = true; - } - else - { + } + case s_graph: { + if (iswgraph(c)) { + consumed = true; // consumed printable non-space + } else { state = s_end; } break; - + } case s_end: - default: - break; + default: { break; } } } return consumed; } -bool move_word_state_machine_t::consume_char(wchar_t c) -{ - switch (style) - { - case move_word_style_punctuation: +bool move_word_state_machine_t::consume_char(wchar_t c) { + switch (style) { + case move_word_style_punctuation: { return consume_char_punctuation(c); - case move_word_style_path_components: + } + case move_word_style_path_components: { return consume_char_path_components(c); - case move_word_style_whitespace: + } + case move_word_style_whitespace: { return consume_char_whitespace(c); - default: - return false; + } + default: { return false; } } } -move_word_state_machine_t::move_word_state_machine_t(move_word_style_t syl) : state(0), style(syl) -{ -} +move_word_state_machine_t::move_word_state_machine_t(move_word_style_t syl) + : state(0), style(syl) {} -void move_word_state_machine_t::reset() -{ - state = 0; -} +void move_word_state_machine_t::reset() { state = 0; } diff --git a/src/tokenizer.h b/src/tokenizer.h index 3c3b62364..c223e438a 100644 --- a/src/tokenizer.h +++ b/src/tokenizer.h @@ -1,191 +1,151 @@ -/** \file tokenizer.h - - A specialized tokenizer for tokenizing the fish language. In the - future, the tokenizer should be extended to support marks, - tokenizing multiple strings and disposing of unused string - segments. -*/ +// A specialized tokenizer for tokenizing the fish language. In the future, the tokenizer should be +// extended to support marks, tokenizing multiple strings and disposing of unused string segments. #ifndef FISH_TOKENIZER_H #define FISH_TOKENIZER_H -#include #include +#include #include "common.h" -/** - Token types -*/ -enum token_type -{ - TOK_NONE, /**< Tokenizer not yet constructed */ - TOK_ERROR, /**< Error reading token */ - TOK_STRING,/**< String token */ - TOK_PIPE,/**< Pipe token */ - TOK_END,/**< End token (semicolon or newline, not literal end) */ - TOK_REDIRECT_OUT, /**< redirection token */ - TOK_REDIRECT_APPEND,/**< redirection append token */ - TOK_REDIRECT_IN,/**< input redirection token */ - TOK_REDIRECT_FD,/**< redirection to new fd token */ - TOK_REDIRECT_NOCLOB, /** Date: Tue, 3 May 2016 15:05:47 -0700 Subject: [PATCH 212/363] restyle utf8 module to match project style Reduces lint errors from 63 to 57 (-10%). Line count from 518 to 418 (-19%). Another step in resolving issue #2902. --- src/utf8.cpp | 398 +++++++++++++++++++-------------------------------- src/utf8.h | 14 +- 2 files changed, 156 insertions(+), 256 deletions(-) diff --git a/src/utf8.cpp b/src/utf8.cpp index 7c876a311..ceec637e9 100644 --- a/src/utf8.cpp +++ b/src/utf8.cpp @@ -13,44 +13,40 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include #include // IWYU pragma: keep -#include +#include #include +#include #include "utf8.h" -#define _NXT 0x80 -#define _SEQ2 0xc0 -#define _SEQ3 0xe0 -#define _SEQ4 0xf0 -#define _SEQ5 0xf8 -#define _SEQ6 0xfc +#define _NXT 0x80 +#define _SEQ2 0xc0 +#define _SEQ3 0xe0 +#define _SEQ4 0xf0 +#define _SEQ5 0xf8 +#define _SEQ6 0xfc -#define _BOM 0xfeff +#define _BOM 0xfeff -/* We can tweak the following typedef to allow us to simulate Windows-style 16 bit wchar's on Unix */ +// We can tweak the following typedef to allow us to simulate Windows-style 16 bit wchar's on Unix. typedef wchar_t utf8_wchar_t; #define UTF8_WCHAR_MAX ((size_t)std::numeric_limits::max()) typedef std::basic_string utf8_wstring_t; -bool is_wchar_ucs2() -{ - return UTF8_WCHAR_MAX <= 0xFFFF; -} +bool is_wchar_ucs2() { return UTF8_WCHAR_MAX <= 0xFFFF; } -static size_t utf8_to_wchar_internal(const char *in, size_t insize, utf8_wstring_t *result, int flags); -static size_t wchar_to_utf8_internal(const utf8_wchar_t *in, size_t insize, char *out, size_t outsize, int flags); +static size_t utf8_to_wchar_internal(const char *in, size_t insize, utf8_wstring_t *result, + int flags); +static size_t wchar_to_utf8_internal(const utf8_wchar_t *in, size_t insize, char *out, + size_t outsize, int flags); -static bool safe_copy_wchar_to_utf8_wchar(const wchar_t *in, utf8_wchar_t *out, size_t count) -{ +static bool safe_copy_wchar_to_utf8_wchar(const wchar_t *in, utf8_wchar_t *out, size_t count) { bool result = true; - for (size_t i=0; i < count; i++) - { + for (size_t i = 0; i < count; i++) { wchar_t c = in[i]; - if (c > UTF8_WCHAR_MAX) - { + if (c > UTF8_WCHAR_MAX) { result = false; break; } @@ -59,24 +55,20 @@ static bool safe_copy_wchar_to_utf8_wchar(const wchar_t *in, utf8_wchar_t *out, return result; } -bool wchar_to_utf8_string(const std::wstring &str, std::string *result) -{ +bool wchar_to_utf8_string(const std::wstring &str, std::string *result) { result->clear(); const size_t inlen = str.size(); - if (inlen == 0) - { + if (inlen == 0) { return true; } bool success = false; const wchar_t *input = str.c_str(); size_t outlen = wchar_to_utf8(input, inlen, NULL, 0, 0); - if (outlen > 0) - { + if (outlen > 0) { char *tmp = new char[outlen]; size_t outlen2 = wchar_to_utf8(input, inlen, tmp, outlen, 0); - if (outlen2 > 0) - { + if (outlen2 > 0) { result->assign(tmp, outlen2); success = true; } @@ -85,27 +77,19 @@ bool wchar_to_utf8_string(const std::wstring &str, std::string *result) return success; } -size_t utf8_to_wchar(const char *in, size_t insize, std::wstring *out, int flags) -{ - if (in == NULL || insize == 0) - { +size_t utf8_to_wchar(const char *in, size_t insize, std::wstring *out, int flags) { + if (in == NULL || insize == 0) { return 0; } size_t result; - if (sizeof(wchar_t) == sizeof(utf8_wchar_t)) - { + if (sizeof(wchar_t) == sizeof(utf8_wchar_t)) { result = utf8_to_wchar_internal(in, insize, reinterpret_cast(out), flags); - } - else if (out == NULL) - { + } else if (out == NULL) { result = utf8_to_wchar_internal(in, insize, NULL, flags); - } - else - { - // Allocate a temporary buffer to hold the output, - // invoke the conversion with the temporary, - // and then copy it back + } else { + // Allocate a temporary buffer to hold the output, invoke the conversion with the temporary, + // and then copy it back. utf8_wstring_t tmp_output; result = utf8_to_wchar_internal(in, insize, &tmp_output, flags); out->insert(out->end(), tmp_output.begin(), tmp_output.end()); @@ -113,32 +97,24 @@ size_t utf8_to_wchar(const char *in, size_t insize, std::wstring *out, int flags return result; } -size_t wchar_to_utf8(const wchar_t *in, size_t insize, char *out, size_t outsize, int flags) -{ - if (in == NULL || insize == 0 || (outsize == 0 && out != NULL)) - { +size_t wchar_to_utf8(const wchar_t *in, size_t insize, char *out, size_t outsize, int flags) { + if (in == NULL || insize == 0 || (outsize == 0 && out != NULL)) { return 0; } size_t result; - if (sizeof(wchar_t) == sizeof(utf8_wchar_t)) - { - result = wchar_to_utf8_internal(reinterpret_cast(in), insize, out, outsize, flags); - } - else - { - // Allocate a temporary buffer to hold the input - // the std::copy performs the size conversion - // note: insize may be 0 + if (sizeof(wchar_t) == sizeof(utf8_wchar_t)) { + result = wchar_to_utf8_internal(reinterpret_cast(in), insize, out, + outsize, flags); + } else { + // Allocate a temporary buffer to hold the input the std::copy performs the size conversion. + // Note: insize may be 0. utf8_wchar_t *tmp_input = new utf8_wchar_t[insize]; - if (! safe_copy_wchar_to_utf8_wchar(in, tmp_input, insize)) - { - // our utf8_wchar_t is UCS-16 and there was an astral character + if (!safe_copy_wchar_to_utf8_wchar(in, tmp_input, insize)) { + // Our utf8_wchar_t is UCS-16 and there was an astral character. result = 0; - } - else - { - // Invoke the conversion with the temporary, then clean up the input + } else { + // Invoke the conversion with the temporary, then clean up the input. result = wchar_to_utf8_internal(tmp_input, insize, out, outsize, flags); } delete[] tmp_input; @@ -146,191 +122,136 @@ size_t wchar_to_utf8(const wchar_t *in, size_t insize, char *out, size_t outsize return result; } - static int __wchar_forbitten(utf8_wchar_t sym); static int __utf8_forbitten(unsigned char octet); -static int -__wchar_forbitten(utf8_wchar_t sym) -{ - - /* Surrogate pairs */ - if (sym >= 0xd800 && sym <= 0xdfff) - return (-1); +static int __wchar_forbitten(utf8_wchar_t sym) { + // Surrogate pairs. + if (sym >= 0xd800 && sym <= 0xdfff) return (-1); return (0); } -static int -__utf8_forbitten(unsigned char octet) -{ - - switch (octet) - { +static int __utf8_forbitten(unsigned char octet) { + switch (octet) { case 0xc0: case 0xc1: case 0xf5: - case 0xff: + case 0xff: { return (-1); + } } return (0); } -/* - * DESCRIPTION - * This function translates UTF-8 string into UCS-2 or UCS-4 string (all symbols - * will be in local machine byte order). - * - * It takes the following arguments: - * in - input UTF-8 string. It can be null-terminated. - * insize - size of input string in bytes. - * out_string - result buffer for UCS-2/4 string. - * - * RETURN VALUES - * The function returns size of result buffer (in wide characters). - * Zero is returned in case of error. - * - * CAVEATS - * 1. If UTF-8 string contains zero symbols, they will be translated - * as regular symbols. - * 2. If UTF8_IGNORE_ERROR or UTF8_SKIP_BOM flag is set, sizes may vary - * when `out' is NULL and not NULL. It's because of special UTF-8 - * sequences which may result in forbitten (by RFC3629) UNICODE - * characters. So, the caller must check return value every time and - * not prepare buffer in advance (\0 terminate) but after calling this - * function. - */ -static size_t utf8_to_wchar_internal(const char *in, size_t insize, utf8_wstring_t *out_string, int flags) -{ +/// This function translates UTF-8 string into UCS-2 or UCS-4 string (all symbols will be in local +/// machine byte order). It takes the following arguments: +/// +/// in - input UTF-8 string. It can be null-terminated. +/// insize - size of input string in bytes. +/// out_string - result buffer for UCS-2/4 string. +/// +/// RETURN VALUES +/// The function returns size of result buffer (in wide characters). +/// Zero is returned in case of error. +/// +/// CAVEATS +/// 1. If UTF-8 string contains zero symbols, they will be translated +/// as regular symbols. +/// +/// 2. If UTF8_IGNORE_ERROR or UTF8_SKIP_BOM flag is set, sizes may vary when `out' is NULL and not +/// NULL. It's because of special UTF-8 sequences which may result in forbitten (by RFC3629) UNICODE +/// characters. So, the caller must check return value every time and not prepare buffer in advance +/// (\0 terminate) but after calling this function. +static size_t utf8_to_wchar_internal(const char *in, size_t insize, utf8_wstring_t *out_string, + int flags) { unsigned char *p, *lim; utf8_wchar_t high; size_t n, total, i, n_bits; - if (in == NULL || insize == 0) - return (0); - - if (out_string != NULL) - out_string->clear(); + if (in == NULL || insize == 0) return (0); + + if (out_string != NULL) out_string->clear(); total = 0; p = (unsigned char *)in; lim = p + insize; - for (; p < lim; p += n) - { - if (__utf8_forbitten(*p) != 0 && - (flags & UTF8_IGNORE_ERROR) == 0) - return (0); + for (; p < lim; p += n) { + if (__utf8_forbitten(*p) != 0 && (flags & UTF8_IGNORE_ERROR) == 0) return (0); - /* - * Get number of bytes for one wide character. - */ - n = 1; /* default: 1 byte. Used when skipping bytes. */ + // Get number of bytes for one wide character. + n = 1; // default: 1 byte. Used when skipping bytes if ((*p & 0x80) == 0) high = (utf8_wchar_t)*p; - else if ((*p & 0xe0) == _SEQ2) - { + else if ((*p & 0xe0) == _SEQ2) { n = 2; high = (utf8_wchar_t)(*p & 0x1f); - } - else if ((*p & 0xf0) == _SEQ3) - { + } else if ((*p & 0xf0) == _SEQ3) { n = 3; high = (utf8_wchar_t)(*p & 0x0f); - } - else if ((*p & 0xf8) == _SEQ4) - { + } else if ((*p & 0xf8) == _SEQ4) { n = 4; high = (utf8_wchar_t)(*p & 0x07); - } - else if ((*p & 0xfc) == _SEQ5) - { + } else if ((*p & 0xfc) == _SEQ5) { n = 5; high = (utf8_wchar_t)(*p & 0x03); - } - else if ((*p & 0xfe) == _SEQ6) - { + } else if ((*p & 0xfe) == _SEQ6) { n = 6; high = (utf8_wchar_t)(*p & 0x01); - } - else - { - if ((flags & UTF8_IGNORE_ERROR) == 0) - return (0); + } else { + if ((flags & UTF8_IGNORE_ERROR) == 0) return (0); continue; } - /* does the sequence header tell us truth about length? */ - if (lim - p <= n - 1) - { - if ((flags & UTF8_IGNORE_ERROR) == 0) - return (0); + // Does the sequence header tell us truth about length? + if (lim - p <= n - 1) { + if ((flags & UTF8_IGNORE_ERROR) == 0) return (0); n = 1; - continue; /* skip */ + continue; // skip } - /* - * Validate sequence. - * All symbols must have higher bits set to 10xxxxxx - */ - if (n > 1) - { - for (i = 1; i < n; i++) - { - if ((p[i] & 0xc0) != _NXT) - break; + // Validate sequence. All symbols must have higher bits set to 10xxxxxx. + if (n > 1) { + for (i = 1; i < n; i++) { + if ((p[i] & 0xc0) != _NXT) break; } - if (i != n) - { - if ((flags & UTF8_IGNORE_ERROR) == 0) - return (0); + if (i != n) { + if ((flags & UTF8_IGNORE_ERROR) == 0) return (0); n = 1; - continue; /* skip */ + continue; // skip } } total++; - if (out_string == NULL) - continue; + if (out_string == NULL) continue; uint32_t out_val = 0; n_bits = 0; - for (i = 1; i < n; i++) - { + for (i = 1; i < n; i++) { out_val |= (utf8_wchar_t)(p[n - i] & 0x3f) << n_bits; - n_bits += 6; /* 6 low bits in every byte */ + n_bits += 6; // 6 low bits in every byte } out_val |= high << n_bits; bool skip = false; - if (__wchar_forbitten(out_val) != 0) - { - if ((flags & UTF8_IGNORE_ERROR) == 0) - { - return 0; /* forbitten character */ - } - else - { + if (__wchar_forbitten(out_val) != 0) { + if ((flags & UTF8_IGNORE_ERROR) == 0) { + return 0; // forbidden character + } else { skip = true; } - } - else if (out_val == _BOM && (flags & UTF8_SKIP_BOM) != 0) - { + } else if (out_val == _BOM && (flags & UTF8_SKIP_BOM) != 0) { skip = true; } - if (skip) - { + if (skip) { total--; - } - else if (out_val > UTF8_WCHAR_MAX) - { - // wchar_t is UCS-2, but the UTF-8 specified an astral character + } else if (out_val > UTF8_WCHAR_MAX) { + // wchar_t is UCS-2, but the UTF-8 specified an astral character. return 0; - } - else - { + } else { out_string->push_back(out_val); } } @@ -338,61 +259,47 @@ static size_t utf8_to_wchar_internal(const char *in, size_t insize, utf8_wstring return (total); } -/* - * DESCRIPTION - * This function translates UCS-2/4 symbols (given in local machine - * byte order) into UTF-8 string. - * - * It takes the following arguments: - * in - input unicode string. It can be null-terminated. - * insize - size of input string in wide characters. - * out - result buffer for utf8 string. If out is NULL, - * function returns size of result buffer. - * outsize - size of result buffer. - * - * RETURN VALUES - * The function returns size of result buffer (in bytes). Zero is returned - * in case of error. - * - * CAVEATS - * If UCS-4 string contains zero symbols, they will be translated - * as regular symbols. - */ -static size_t wchar_to_utf8_internal(const utf8_wchar_t *in, size_t insize, char *out, size_t outsize, int flags) -{ +/// This function translates UCS-2/4 symbols (given in local machine byte order) into UTF-8 string. +/// It takes the following arguments: +/// +/// in - input unicode string. It can be null-terminated. +/// insize - size of input string in wide characters. +/// out - result buffer for utf8 string. If out is NULL, function returns size of result buffer. +/// outsize - size of result buffer. +/// +/// RETURN VALUES +/// The function returns size of result buffer (in bytes). Zero is returned in case of error. +/// +/// CAVEATS +/// If UCS-4 string contains zero symbols, they will be translated as regular symbols. +static size_t wchar_to_utf8_internal(const utf8_wchar_t *in, size_t insize, char *out, + size_t outsize, int flags) { const utf8_wchar_t *w, *wlim; unsigned char *p, *lim; size_t total, n; - if (in == NULL || insize == 0 || (outsize == 0 && out != NULL)) - return (0); + if (in == NULL || insize == 0 || (outsize == 0 && out != NULL)) return (0); w = in; wlim = w + insize; p = (unsigned char *)out; lim = p + outsize; total = 0; - for (; w < wlim; w++) - { - if (__wchar_forbitten(*w) != 0) - { + for (; w < wlim; w++) { + if (__wchar_forbitten(*w) != 0) { if ((flags & UTF8_IGNORE_ERROR) == 0) return (0); else continue; } - if (*w == _BOM && (flags & UTF8_SKIP_BOM) != 0) - continue; + if (*w == _BOM && (flags & UTF8_SKIP_BOM) != 0) continue; const int32_t w_wide = *w; - if (w_wide < 0) - { - if ((flags & UTF8_IGNORE_ERROR) == 0) - return (0); + if (w_wide < 0) { + if ((flags & UTF8_IGNORE_ERROR) == 0) return (0); continue; - } - else if (w_wide <= 0x0000007f) + } else if (w_wide <= 0x0000007f) n = 1; else if (w_wide <= 0x000007ff) n = 2; @@ -402,18 +309,16 @@ static size_t wchar_to_utf8_internal(const utf8_wchar_t *in, size_t insize, char n = 4; else if (w_wide <= 0x03ffffff) n = 5; - else /* if (w_wide <= 0x7fffffff) */ + else /// if (w_wide <= 0x7fffffff) n = 6; total += n; - if (out == NULL) - continue; + if (out == NULL) continue; - if (lim - p <= n - 1) - return (0); /* no space left */ + if (lim - p <= n - 1) return (0); /* no space left */ - /* extract the wchar_t as big-endian. If wchar_t is UCS-16, the first two bytes will be 0 */ + // Extract the wchar_t as big-endian. If wchar_t is UCS-16, the first two bytes will be 0. unsigned char oc[4]; uint32_t w_tmp = *w; oc[3] = w_tmp & 0xFF; @@ -424,41 +329,38 @@ static size_t wchar_to_utf8_internal(const utf8_wchar_t *in, size_t insize, char w_tmp >>= 8; oc[0] = w_tmp & 0xFF; - switch (n) - { - case 1: + switch (n) { + case 1: { p[0] = oc[3]; break; - - case 2: + } + case 2: { p[1] = _NXT | (oc[3] & 0x3f); p[0] = _SEQ2 | (oc[3] >> 6) | ((oc[2] & 0x07) << 2); break; - - case 3: + } + case 3: { p[2] = _NXT | (oc[3] & 0x3f); p[1] = _NXT | (oc[3] >> 6) | ((oc[2] & 0x0f) << 2); p[0] = _SEQ3 | ((oc[2] & 0xf0) >> 4); break; - - case 4: + } + case 4: { p[3] = _NXT | (oc[3] & 0x3f); p[2] = _NXT | (oc[3] >> 6) | ((oc[2] & 0x0f) << 2); - p[1] = _NXT | ((oc[2] & 0xf0) >> 4) | - ((oc[1] & 0x03) << 4); + p[1] = _NXT | ((oc[2] & 0xf0) >> 4) | ((oc[1] & 0x03) << 4); p[0] = _SEQ4 | ((oc[1] & 0x1f) >> 2); break; - - case 5: + } + case 5: { p[4] = _NXT | (oc[3] & 0x3f); p[3] = _NXT | (oc[3] >> 6) | ((oc[2] & 0x0f) << 2); - p[2] = _NXT | ((oc[2] & 0xf0) >> 4) | - ((oc[1] & 0x03) << 4); + p[2] = _NXT | ((oc[2] & 0xf0) >> 4) | ((oc[1] & 0x03) << 4); p[1] = _NXT | (oc[1] >> 2); p[0] = _SEQ5 | (oc[0] & 0x03); break; - - case 6: + } + case 6: { p[5] = _NXT | (oc[3] & 0x3f); p[4] = _NXT | (oc[3] >> 6) | ((oc[2] & 0x0f) << 2); p[3] = _NXT | (oc[2] >> 4) | ((oc[1] & 0x03) << 4); @@ -466,13 +368,11 @@ static size_t wchar_to_utf8_internal(const utf8_wchar_t *in, size_t insize, char p[1] = _NXT | (oc[0] & 0x3f); p[0] = _SEQ6 | ((oc[0] & 0x40) >> 6); break; + } } - /* - * NOTE: do not check here for forbitten UTF-8 characters. - * They cannot appear here because we do proper convertion. - */ - + // NOTE: do not check here for forbitten UTF-8 characters. They cannot appear here because + // we do proper convertion. p += n; } diff --git a/src/utf8.h b/src/utf8.h index 72e1cc8bd..99a12a8e6 100644 --- a/src/utf8.h +++ b/src/utf8.h @@ -14,22 +14,22 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* - * utf8: implementation of UTF-8 charset encoding (RFC3629). - */ +// Implementation of UTF-8 charset encoding (RFC3629). #ifndef _UTF8_H_ #define _UTF8_H_ #include #include -#define UTF8_IGNORE_ERROR 0x01 -#define UTF8_SKIP_BOM 0x02 +#define UTF8_IGNORE_ERROR 0x01 +#define UTF8_SKIP_BOM 0x02 -/* Convert a string between UTF8 and UCS-2/4 (depending on size of wchar_t). Returns true if successful, storing the result of the conversion in *result */ +/// Convert a string between UTF8 and UCS-2/4 (depending on size of wchar_t). Returns true if +/// successful, storing the result of the conversion in *result*. bool wchar_to_utf8_string(const std::wstring &input, std::string *result); -/* Convert a string between UTF8 and UCS-2/4 (depending on size of wchar_t). Returns nonzero if successful, storing the result of the conversion in *out */ +/// Convert a string between UTF8 and UCS-2/4 (depending on size of wchar_t). Returns nonzero if +/// successful, storing the result of the conversion in *out*. size_t utf8_to_wchar(const char *in, size_t insize, std::wstring *out, int flags); size_t wchar_to_utf8(const wchar_t *in, size_t insize, char *out, size_t outsize, int flags); From 5c8763be0e68bbdec3fdd1edb5754f4c421098e1 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Tue, 3 May 2016 15:18:24 -0700 Subject: [PATCH 213/363] restyle remaining modules to match project style For this change I decided to bundle the remaining modules that need to be resytyled because only two were large enough to warrant doing on their own. Reduces lint errors from 225 to 162 (-28%). Line count from 3073 to 2465 (-20%). Another step in resolving issue #2902. --- src/util.cpp | 80 +-- src/util.h | 92 ++- src/wcstringutil.cpp | 23 +- src/wcstringutil.h | 31 +- src/wgetopt.cpp | 630 +++++++++----------- src/wgetopt.h | 295 ++++------ src/wildcard.cpp | 1297 +++++++++++++++++------------------------- src/wildcard.h | 111 ++-- src/wutil.cpp | 406 +++++-------- src/wutil.h | 139 ++--- 10 files changed, 1248 insertions(+), 1856 deletions(-) diff --git a/src/util.cpp b/src/util.cpp index 9d084af67..316bac68c 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -1,39 +1,32 @@ -/** \file util.c - Generic utilities library. - - Contains datastructures such as automatically growing array lists, priority queues, etc. -*/ +// Generic utilities library. +// +// Contains data structures such as automatically growing array lists, priority queues, etc. +#include #include +#include +#include #include #include -#include -#include -#include +#include "common.h" #include "fallback.h" // IWYU pragma: keep #include "util.h" -#include "common.h" #include "wutil.h" // IWYU pragma: keep -int wcsfilecmp(const wchar_t *a, const wchar_t *b) -{ +int wcsfilecmp(const wchar_t *a, const wchar_t *b) { CHECK(a, 0); CHECK(b, 0); - if (*a==0) - { - if (*b==0) - return 0; + if (*a == 0) { + if (*b == 0) return 0; return -1; } - if (*b==0) - { + if (*b == 0) { return 1; } - long secondary_diff=0; - if (iswdigit(*a) && iswdigit(*b)) - { + long secondary_diff = 0; + if (iswdigit(*a) && iswdigit(*b)) { wchar_t *aend, *bend; long al; long bl; @@ -43,53 +36,40 @@ int wcsfilecmp(const wchar_t *a, const wchar_t *b) al = wcstol(a, &aend, 10); bl = wcstol(b, &bend, 10); - if (errno) - { - /* - Huuuuuuuuge numbers - fall back to regular string comparison - */ + if (errno) { + // Huge numbers - fall back to regular string comparison. return wcscmp(a, b); } diff = al - bl; - if (diff) - return diff > 0 ? 2 : -2; + if (diff) return diff > 0 ? 2 : -2; - secondary_diff = (aend-a) - (bend-b); + secondary_diff = (aend - a) - (bend - b); - a=aend-1; - b=bend-1; - } - else - { + a = aend - 1; + b = bend - 1; + } else { int diff = towlower(*a) - towlower(*b); - if (diff != 0) - return (diff>0)?2:-2; + if (diff != 0) return (diff > 0) ? 2 : -2; - secondary_diff = *a-*b; + secondary_diff = *a - *b; } - int res = wcsfilecmp(a+1, b+1); + 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; + 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() -{ +long long get_time() { struct timeval time_struct; gettimeofday(&time_struct, 0); - return 1000000ll*time_struct.tv_sec+time_struct.tv_usec; + return 1000000ll * time_struct.tv_sec + time_struct.tv_usec; } - diff --git a/src/util.h b/src/util.h index d073ef83c..9d2e4bbbb 100644 --- a/src/util.h +++ b/src/util.h @@ -1,67 +1,49 @@ -/** \file util.h - Generic utilities library. -*/ - +// Generic utilities library. #ifndef FISH_UTIL_H #define FISH_UTIL_H -/** - Returns the larger of two ints -*/ -template -inline T maxi(T a, T b) -{ - return a>b?a:b; +/// Returns the larger of two ints. +template +inline T maxi(T a, T b) { + return a > b ? a : b; } -/** - Returns the smaller of two ints - */ -template -inline T mini(T a, T b) -{ - return a +inline T mini(T a, T b) { + return a < b ? a : b; } -/** - Compares two wide character strings with an (arguably) intuitive - ordering. - - This function tries to order strings in a way which is intuitive to - humans with regards to sorting strings containing numbers. - - Most sorting functions would sort the strings 'file1.txt' - 'file5.txt' and 'file12.txt' as: - - file1.txt - file12.txt - file5.txt - - This function regards any sequence of digits as a single entity - when performing comparisons, so the output is instead: - - file1.txt - file5.txt - file12.txt - - Which most people would find more intuitive. - - This won't return the optimum results for numbers in bases higher - than ten, such as hexadecimal, but at least a stable sort order - will result. - - This function performs a two-tiered sort, where difference in case - and in number of leading zeroes in numbers only have effect if no - other differences between strings are found. This way, a 'file1' - and 'File1' will not be considered identical, and hence their - internal sort order is not arbitrary, but the names 'file1', - 'File2' and 'file3' will still be sorted in the order given above. -*/ +/// Compares two wide character strings with an (arguably) intuitive ordering. This function tries +/// to order strings in a way which is intuitive to humans with regards to sorting strings +/// containing numbers. +/// +/// Most sorting functions would sort the strings 'file1.txt' 'file5.txt' and 'file12.txt' as: +/// +/// file1.txt +/// file12.txt +/// file5.txt +/// +/// This function regards any sequence of digits as a single entity when performing comparisons, so +/// the output is instead: +/// +/// file1.txt +/// file5.txt +/// file12.txt +/// +/// Which most people would find more intuitive. +/// +/// This won't return the optimum results for numbers in bases higher than ten, such as hexadecimal, +/// but at least a stable sort order will result. +/// +/// This function performs a two-tiered sort, where difference in case and in number of leading +/// zeroes in numbers only have effect if no other differences between strings are found. This way, +/// a 'file1' and 'File1' will not be considered identical, and hence their internal sort order is +/// not arbitrary, but the names 'file1', 'File2' and 'file3' will still be sorted in the order +/// given above. int wcsfilecmp(const wchar_t *a, const wchar_t *b); -/** - Get the current time in microseconds since Jan 1, 1970 -*/ +/// Get the current time in microseconds since Jan 1, 1970. long long get_time(); #endif diff --git a/src/wcstringutil.cpp b/src/wcstringutil.cpp index b752409b7..3983eb53a 100644 --- a/src/wcstringutil.cpp +++ b/src/wcstringutil.cpp @@ -1,24 +1,18 @@ -/** \file wcstringutil.cpp - -Helper functions for working with wcstring -*/ -#include "common.h" +// Helper functions for working with wcstring. #include "wcstringutil.h" +#include "common.h" typedef wcstring::size_type size_type; -wcstring_range wcstring_tok(wcstring& str, const wcstring &needle, wcstring_range last) -{ +wcstring_range wcstring_tok(wcstring& str, const wcstring& needle, wcstring_range last) { size_type pos = last.second == wcstring::npos ? wcstring::npos : last.first; if (pos != wcstring::npos && last.second != wcstring::npos) pos += last.second; if (pos != wcstring::npos && pos != 0) ++pos; - if (pos == wcstring::npos || pos >= str.size()) - { + if (pos == wcstring::npos || pos >= str.size()) { return std::make_pair(wcstring::npos, wcstring::npos); } - if (needle.empty()) - { + if (needle.empty()) { return std::make_pair(pos, wcstring::npos); } @@ -26,12 +20,9 @@ wcstring_range wcstring_tok(wcstring& str, const wcstring &needle, wcstring_rang if (pos == wcstring::npos) return std::make_pair(wcstring::npos, wcstring::npos); size_type next_pos = str.find_first_of(needle, pos); - if (next_pos == wcstring::npos) - { + if (next_pos == wcstring::npos) { return std::make_pair(pos, wcstring::npos); - } - else - { + } else { str[next_pos] = L'\0'; return std::make_pair(pos, next_pos - pos); } diff --git a/src/wcstringutil.h b/src/wcstringutil.h index d2feec07a..ea4489e76 100644 --- a/src/wcstringutil.h +++ b/src/wcstringutil.h @@ -1,7 +1,4 @@ -/** \file wcstringutil.h - -Helper functions for working with wcstring -*/ +// Helper functions for working with wcstring. #ifndef FISH_WCSTRINGUTIL_H #define FISH_WCSTRINGUTIL_H @@ -10,21 +7,19 @@ Helper functions for working with wcstring #include "common.h" -/** - typedef that represents a range in a wcstring. - The first element is the location, the second is the count. -*/ +/// Typedef that represents a range in a wcstring. The first element is the location, the second is +/// the count. typedef std::pair wcstring_range; -/** - wcstring equivalent of wcstok(). Supports NUL. - For convenience and wcstok() compatibility, the first character of each - token separator is replaced with NUL. - Returns a pair of (pos, count). - Returns (npos, npos) when it's done. - Returns (pos, npos) when the token is already known to be the final token. - Note that the final token may not necessarily return (pos, npos). -*/ -wcstring_range wcstring_tok(wcstring& str, const wcstring &needle, wcstring_range last = wcstring_range(0,0)); +/// wcstring equivalent of wcstok(). Supports NUL. For convenience and wcstok() compatibility, the +/// first character of each token separator is replaced with NUL. +/// +/// Returns a pair of (pos, count). +/// Returns (npos, npos) when it's done. +/// Returns (pos, npos) when the token is already known to be the final token. +/// +/// Note that the final token may not necessarily return (pos, npos). +wcstring_range wcstring_tok(wcstring& str, const wcstring& needle, + wcstring_range last = wcstring_range(0, 0)); #endif diff --git a/src/wgetopt.cpp b/src/wgetopt.cpp index 024767188..d85b5e748 100644 --- a/src/wgetopt.cpp +++ b/src/wgetopt.cpp @@ -1,85 +1,68 @@ -/** \file wgetopt.c - A version of the getopt library for use with wide character strings. - - This is simply the gnu getopt library, but converted for use with - wchar_t instead of char. This is not usually useful since the argv - array is always defined to be of type char**, but in fish, all - internal commands use wide characters and hence this library is - useful. - - If you want to use this version of getopt in your program, - download the fish sourcecode, available at the fish homepage. Extract - the sourcode, copy wgetopt.c and wgetopt.h into your program - directory, include wgetopt.h in your program, and use all the - regular getopt functions, prefixing every function, global - variable and structure with a 'w', and use only wide character - strings. There are no other functional changes in this version of - getopt besides using wide character strings. - - For examples of how to use wgetopt, see the fish builtin - functions, many of which are defined in builtin.c. - -*/ - - -/* Getopt for GNU. - NOTE: getopt is now part of the C library, so if you don't know what - "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu - before changing it! - - Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94 - Free Software Foundation, Inc. - - This file is part of the GNU C Library. Its master source is NOT part of - the C library, however. The master source lives in /gd/gnu/lib. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with the GNU C Library; see the file COPYING.LIB. If - not, write to the Free Software Foundation, Inc., 675 Mass Ave, - Cambridge, MA 02139, USA. */ +// A version of the getopt library for use with wide character strings. +// +// This is simply the gnu getopt library, but converted for use with wchar_t instead of char. This +// is not usually useful since the argv array is always defined to be of type char**, but in fish, +// all internal commands use wide characters and hence this library is useful. +// +// If you want to use this version of getopt in your program, download the fish sourcecode, +// available at the fish homepage. Extract the sourcode, copy +// wgetopt.c and wgetopt.h into your program directory, include wgetopt.h in your program, and use +// all the regular getopt functions, prefixing every function, global variable and structure with a +// 'w', and use only wide character strings. There are no other functional changes in this version +// of getopt besides using wide character strings. +// +// For examples of how to use wgetopt, see the fish builtin functions, many of which are defined in +// builtin.c. +// Getopt for GNU. +// +// NOTE: getopt is now part of the C library, so if you don't know what "Keep this file name-space +// clean" means, talk to roland@gnu.ai.mit.edu before changing it! +// +// Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94 +// Free Software Foundation, Inc. +// +// This file is part of the GNU C Library. Its master source is NOT part of the C library, however. +// The master source lives in /gd/gnu/lib. +// +// The GNU C Library is free software; you can redistribute it and/or modify it under the terms of +// the GNU Library General Public License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +// the GNU Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public License along with the GNU C +// Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 675 Mass +// Ave, Cambridge, MA 02139, USA. #include "config.h" #include #include -/* This needs to come after some library #include - to get __GNU_LIBRARY__ defined. */ -#ifdef __GNU_LIBRARY__ -/* Don't include stdlib.h for non-GNU C libraries because some of them - contain conflicting prototypes for getopt. */ +// This needs to come after some library #include to get __GNU_LIBRARY__ defined. +#ifdef __GNU_LIBRARY__ +// Don't include stdlib.h for non-GNU C libraries because some of them contain conflicting +// prototypes for getopt. #include -#endif /* GNU C library. */ - -/* This version of `getopt' appears to the caller like standard Unix `getopt' - but it behaves differently for the user, since it allows the user - to intersperse the options with the other arguments. - - As `getopt' works, it permutes the elements of ARGV so that, - when it is done, all the options precede everything else. Thus - all application programs are extended to handle flexible argument order. - - GNU application programs can use a third alternative mode in which - they can distinguish the relative order of options and other arguments. */ +#endif // GNU C library. +// This version of `getopt' appears to the caller like standard Unix `getopt' but it behaves +// differently for the user, since it allows the user to intersperse the options with the other +// arguments. +// +// As `getopt' works, it permutes the elements of ARGV so that, when it is done, all the options +// precede everything else. Thus all application programs are extended to handle flexible argument +// order. +// +// GNU application programs can use a third alternative mode in which they can distinguish the +// relative order of options and other arguments. #include "common.h" +#include "fallback.h" // IWYU pragma: keep #include "wgetopt.h" #include "wutil.h" // IWYU pragma: keep -#include "fallback.h" // IWYU pragma: keep -/** - Use translation functions if available -*/ +// Use translation functions if available. #ifdef _ #undef _ #endif @@ -94,229 +77,178 @@ #define _(wstr) wstr #endif -#ifdef __GNU_LIBRARY__ -/* We want to avoid inclusion of string.h with non-GNU libraries - because there are many ways it can cause trouble. - On some systems, it contains special magic macros that don't work - in GCC. */ -#include // IWYU pragma: keep -#define my_index wcschr +#ifdef __GNU_LIBRARY__ +// We want to avoid inclusion of string.h with non-GNU libraries because there are many ways it can +// cause trouble. On some systems, it contains special magic macros that don't work in GCC. +#include // IWYU pragma: keep +#define my_index wcschr #else -/* Avoid depending on library functions or files - whose names are inconsistent. */ +// Avoid depending on library functions or files whose names are inconsistent. char *getenv(); -static wchar_t * -my_index(const wchar_t *str, int chr) -{ - while (*str) - { - if (*str == chr) - return (wchar_t *) str; +static wchar_t *my_index(const wchar_t *str, int chr) { + while (*str) { + if (*str == chr) return (wchar_t *)str; str++; } return 0; } -/* If using GCC, we can safely declare strlen this way. - If not using GCC, it is ok not to declare it. */ +// If using GCC, we can safely declare strlen this way. If not using GCC, it is ok not to declare +// it. #ifdef __GNUC__ -/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. - That was relevant to code that was here before. */ -#if !defined (__STDC__) || !__STDC__ -/* gcc with -traditional declares the built-in strlen to return int, - and has done so at least since version 2.4.5. -- rms. */ +// Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. That was relevant to code that +// was here before. +#if !defined(__STDC__) || !__STDC__ +// gcc with -traditional declares the built-in strlen to return int, and has done so at least since +// version 2.4.5. -- rms. extern int wcslen(const wchar_t *); -#endif /* not __STDC__ */ -#endif /* __GNUC__ */ +#endif // not __STDC__ +#endif // __GNUC__ -#endif /* not __GNU_LIBRARY__ */ +#endif // not __GNU_LIBRARY__ - -/* Exchange two adjacent subsequences of ARGV. - One subsequence is elements [first_nonopt,last_nonopt) - which contains all the non-options that have been skipped so far. - The other is elements [last_nonopt,woptind), which contains all - the options processed since those non-options were skipped. - - `first_nonopt' and `last_nonopt' are relocated so that they describe - the new indices of the non-options in ARGV after they are moved. */ - -void wgetopter_t::exchange(wchar_t **argv) -{ +// Exchange two adjacent subsequences of ARGV. One subsequence is elements +// [first_nonopt,last_nonopt) which contains all the non-options that have been skipped so far. The +// other is elements [last_nonopt,woptind), which contains all the options processed since those +// non-options were skipped. +// +// `first_nonopt' and `last_nonopt' are relocated so that they describe the new indices of the +// non-options in ARGV after they are moved. +void wgetopter_t::exchange(wchar_t **argv) { int bottom = first_nonopt; int middle = last_nonopt; int top = woptind; wchar_t *tem; - /* Exchange the shorter segment with the far end of the longer segment. - That puts the shorter segment into the right place. - It leaves the longer segment in the right place overall, - but it consists of two parts that need to be swapped next. */ + // Exchange the shorter segment with the far end of the longer segment. That puts the shorter + // segment into the right place. It leaves the longer segment in the right place overall, but it + // consists of two parts that need to be swapped next. - while (top > middle && middle > bottom) - { - if (top - middle > middle - bottom) - { - /* Bottom segment is the short one. */ + while (top > middle && middle > bottom) { + if (top - middle > middle - bottom) { + // Bottom segment is the short one. int len = middle - bottom; int i; - /* Swap it with the top part of the top segment. */ - for (i = 0; i < len; 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. */ + // Exclude the moved bottom segment from further swapping. top -= len; - } - else - { - /* Top segment is the short one. */ + } else { + // Top segment is the short one. int len = top - middle; int i; - /* Swap it with the bottom part of the bottom segment. */ - for (i = 0; i < len; 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. */ + // 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; } -/* Initialize the internal data when the first call is made. */ - -const wchar_t * wgetopter_t::_wgetopt_initialize(const wchar_t *optstring) -{ - /* Start processing options with ARGV-element 1 (since ARGV-element 0 - is the program name); the sequence of previously skipped - non-option ARGV-elements is empty. */ - +// Initialize the internal data when the first call is made. +const wchar_t *wgetopter_t::_wgetopt_initialize(const wchar_t *optstring) { + // Start processing options with ARGV-element 1 (since ARGV-element 0 is the program name); the + // sequence of previously skipped non-option ARGV-elements is empty. first_nonopt = last_nonopt = woptind = 1; - nextchar = NULL; - /* Determine how to handle the ordering of options and nonoptions. */ - - if (optstring[0] == '-') - { + // Determine how to handle the ordering of options and nonoptions. + if (optstring[0] == '-') { ordering = RETURN_IN_ORDER; ++optstring; - } - else if (optstring[0] == '+') - { + } else if (optstring[0] == '+') { ordering = REQUIRE_ORDER; ++optstring; - } - else + } else ordering = PERMUTE; return optstring; } -/* Scan elements of ARGV (whose length is ARGC) for option characters - given in OPTSTRING. - - If an element of ARGV starts with '-', and is not exactly "-" or "--", - then it is an option element. The characters of this element - (aside from the initial '-') are option characters. If `getopt' - is called repeatedly, it returns successively each of the option characters - from each of the option elements. - - If `getopt' finds another option character, it returns that character, - updating `woptind' and `nextchar' so that the next call to `getopt' can - resume the scan with the following option character or ARGV-element. - - If there are no more option characters, `getopt' returns `EOF'. - Then `woptind' is the index in ARGV of the first ARGV-element - that is not an option. (The ARGV-elements have been permuted - so that those that are not options now come last.) - - OPTSTRING is a string containing the legitimate option characters. - If an option character is seen that is not listed in OPTSTRING, - return '?' after printing an error message. If you set `wopterr' to - zero, the error message is suppressed but we still return '?'. - - If a char in OPTSTRING is followed by a colon, that means it wants an arg, - so the following text in the same ARGV-element, or the text of the following - ARGV-element, is returned in `optarg'. Two colons mean an option that - wants an optional arg; if there is text in the current ARGV-element, - it is returned in `w.woptarg', otherwise `w.woptarg' is set to zero. - - If OPTSTRING starts with `-' or `+', it requests different methods of - handling the non-option ARGV-elements. - See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. - - Long-named options begin with `--' instead of `-'. - Their names may be abbreviated as long as the abbreviation is unique - or is an exact match for some defined option. If they have an - argument, it follows the option name in the same ARGV-element, separated - from the option name by a `=', or else the in next ARGV-element. - When `getopt' finds a long-named option, it returns 0 if that option's - `flag' field is nonzero, the value of the option's `val' field - if the `flag' field is zero. - - LONGOPTS is a vector of `struct option' terminated by an - element containing a name which is zero. - - LONGIND returns the index in LONGOPT of the long-named option found. - It is only valid when a long-named option has been found by the most - recent call. - - If LONG_ONLY is nonzero, '-' as well as '--' can introduce - long-named options. */ - -int wgetopter_t::_wgetopt_internal(int argc, wchar_t **argv, const wchar_t *optstring, const struct woption *longopts, int *longind, int long_only) -{ +// Scan elements of ARGV (whose length is ARGC) for option characters given in OPTSTRING. +// +// If an element of ARGV starts with '-', and is not exactly "-" or "--", then it is an option +// element. The characters of this element (aside from the initial '-') are option characters. If +// `getopt' is called repeatedly, it returns successively each of the option characters from each of +// the option elements. +// +// If `getopt' finds another option character, it returns that character, updating `woptind' and +// `nextchar' so that the next call to `getopt' can resume the scan with the following option +// character or ARGV-element. +// +// If there are no more option characters, `getopt' returns `EOF'. Then `woptind' is the index in +// ARGV of the first ARGV-element that is not an option. (The ARGV-elements have been permuted so +// that those that are not options now come last.) +// +// OPTSTRING is a string containing the legitimate option characters. If an option character is seen +// that is not listed in OPTSTRING, return '?' after printing an error message. If you set +// `wopterr' to zero, the error message is suppressed but we still return '?'. +// +// If a char in OPTSTRING is followed by a colon, that means it wants an arg, so the following text +// in the same ARGV-element, or the text of the following ARGV-element, is returned in `optarg'. +// Two colons mean an option that wants an optional arg; if there is text in the current +// ARGV-element, it is returned in `w.woptarg', otherwise `w.woptarg' is set to zero. +// +// If OPTSTRING starts with `-' or `+', it requests different methods of handling the non-option +// ARGV-elements. See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. +// +// Long-named options begin with `--' instead of `-'. Their names may be abbreviated as long as the +// abbreviation is unique or is an exact match for some defined option. If they have an argument, +// it follows the option name in the same ARGV-element, separated from the option name by a `=', or +// else the in next ARGV-element. When `getopt' finds a long-named option, it returns 0 if that +// option's `flag' field is nonzero, the value of the option's `val' field if the `flag' field is +// zero. +// +// LONGOPTS is a vector of `struct option' terminated by an element containing a name which is zero. +// +// LONGIND returns the index in LONGOPT of the long-named option found. It is only valid when a +// long-named option has been found by the most recent call. +// +// If LONG_ONLY is nonzero, '-' as well as '--' can introduce long-named options. +int wgetopter_t::_wgetopt_internal(int argc, wchar_t **argv, const wchar_t *optstring, + const struct woption *longopts, int *longind, int long_only) { woptarg = NULL; - if (woptind == 0) - optstring = _wgetopt_initialize(optstring); - - if (nextchar == NULL || *nextchar == '\0') - { - /* 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 (woptind == 0) optstring = _wgetopt_initialize(optstring); + if (nextchar == NULL || *nextchar == '\0') { + // 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(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')) + // 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"--")) - { + // 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) @@ -328,241 +260,187 @@ int wgetopter_t::_wgetopt_internal(int argc, wchar_t **argv, const wchar_t *opts 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 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; + 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; + // 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] == '-')); + // 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. */ + // 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]))))) - { + // 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 indfound = 0; // set to zero by Anton int option_index; - for (nameend = nextchar; *nameend && *nameend != '='; nameend++) - /* Do nothing. */ ; + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) /* Do nothing. */; - /* Test all long options for either exact match - or abbreviated matches. */ + // 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. */ + 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. */ + } else if (pfound == NULL) { + // First nonexact match found. pfound = p; indfound = option_index; - } - else - /* Second or later nonexact match found. */ + } else + // Second or later nonexact match found. ambig = 1; } - if (ambig && !exact) - { + if (ambig && !exact) { if (wopterr) - fwprintf(stderr, _(L"%ls: Option '%ls' is ambiguous\n"), - argv[0], argv[woptind]); + fwprintf(stderr, _(L"%ls: Option '%ls' is ambiguous\n"), argv[0], argv[woptind]); nextchar += wcslen(nextchar); woptind++; return '?'; } - if (pfound != NULL) - { + 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 (*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"), + 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"), + // +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) - { + } else if (pfound->has_arg == 1) { if (woptind < argc) woptarg = argv[woptind++]; - else - { + else { if (wopterr) - fwprintf(stderr, _(L"%ls: Option '%ls' requires an argument\n"), - argv[0], argv[woptind - 1]); + 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) - { + 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); + // 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); + // +option or -option + fwprintf(stderr, _(L"%ls: Unrecognized option '%lc%ls'\n"), argv[0], + argv[woptind][0], nextchar); } - nextchar = (wchar_t *) L""; + nextchar = (wchar_t *)L""; woptind++; return '?'; } } - /* Look at and handle the next short option-character. */ - + // Look at and handle the next short option-character. { wchar_t c = *nextchar++; - wchar_t *temp = const_cast(my_index(optstring, c)); + wchar_t *temp = const_cast(my_index(optstring, c)); - /* Increment `woptind' when we start to process its last character. */ - if (*nextchar == '\0') - ++woptind; + // Increment `woptind' when we start to process its last character. + if (*nextchar == '\0') ++woptind; - if (temp == NULL || c == ':') - { - if (wopterr) - { + if (temp == NULL || c == ':') { + if (wopterr) { fwprintf(stderr, _(L"%ls: Invalid option -- %lc\n"), argv[0], (wint_t)c); } woptopt = c; - if (*nextchar != '\0') - woptind++; + if (*nextchar != '\0') woptind++; return '?'; } - if (temp[1] == ':') - { - if (temp[2] == ':') - { - /* This is an option that accepts an argument optionally. */ - if (*nextchar != '\0') - { + if (temp[1] == ':') { + if (temp[2] == ':') { + // This is an option that accepts an argument optionally. + if (*nextchar != '\0') { woptarg = nextchar; woptind++; - } - else + } else woptarg = NULL; nextchar = NULL; - } - else - { - /* This is an option that requires an argument. */ - if (*nextchar != '\0') - { + } 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. */ + // 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], (wint_t)c); + } 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], + (wint_t)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. */ + } else + // We already incremented `woptind' once; increment it again when taking next + // ARGV-elt as argument. woptarg = argv[woptind++]; nextchar = NULL; } @@ -571,12 +449,12 @@ int wgetopter_t::_wgetopt_internal(int argc, wchar_t **argv, const wchar_t *opts } } -int wgetopter_t::wgetopt_long(int argc, wchar_t **argv, const wchar_t *options, const struct woption *long_options, int *opt_index) -{ +int wgetopter_t::wgetopt_long(int argc, wchar_t **argv, const wchar_t *options, + const struct woption *long_options, int *opt_index) { return _wgetopt_internal(argc, argv, options, long_options, opt_index, 0); } -int wgetopter_t::wgetopt_long_only(int argc, wchar_t **argv, const wchar_t *options, const struct woption *long_options, int *opt_index) -{ +int wgetopter_t::wgetopt_long_only(int argc, wchar_t **argv, const wchar_t *options, + const struct woption *long_options, int *opt_index) { return _wgetopt_internal(argc, argv, options, long_options, opt_index, 1); } diff --git a/src/wgetopt.h b/src/wgetopt.h index d20529bbe..95e1462d9 100644 --- a/src/wgetopt.h +++ b/src/wgetopt.h @@ -1,27 +1,18 @@ -/** \file wgetopt.h - A version of the getopt library for use with wide character strings. - - This is simply the gnu getopt library, but converted for use with - wchar_t instead of char. This is not usually useful since the argv - array is always defined to be of type char**, but in fish, all - internal commands use wide characters and hence this library is - useful. - - If you want to use this version of getopt in your program, - download the fish sourcecode, available at the fish homepage. Extract - the sourcode, copy wgetopt.c and wgetopt.h into your program - directory, include wgetopt.h in your program, and use all the - regular getopt functions, prefixing every function, global - variable and structure with a 'w', and use only wide character - strings. There are no other functional changes in this version of - getopt besides using wide character strings. - - For examples of how to use wgetopt, see the fish builtin - functions, many of which are defined in builtin.c. - -*/ - +// A version of the getopt library for use with wide character strings. +// +// This is simply the gnu getopt library, but converted for use with wchar_t instead of char. This +// is not usually useful since the argv array is always defined to be of type char**, but in fish, +// all internal commands use wide characters and hence this library is useful. +// +// If you want to use this version of getopt in your program, download the fish sourcecode, +// available at the fish homepage. Extract the sourcode, copy +// wgetopt.c and wgetopt.h into your program directory, include wgetopt.h in your program, and use +// all the regular getopt functions, prefixing every function, global variable and structure with a +// 'w', and use only wide character strings. There are no other functional changes in this version +// of getopt besides using wide character strings. +// +// For examples of how to use wgetopt, see the fish builtin functions, many of which are defined in +// builtin.c. /* Declarations for getopt. Copyright (C) 1989, 90, 91, 92, 93, 94 Free Software Foundation, Inc. @@ -49,169 +40,131 @@ Cambridge, MA 02139, USA. */ #include -class wgetopter_t -{ -private: +class wgetopter_t { + private: void exchange(wchar_t **argv); - const wchar_t * _wgetopt_initialize(const wchar_t *optstring); - int _wgetopt_internal(int argc, wchar_t **argv, const wchar_t *optstring, const struct woption *longopts, int *longind, int long_only); - -public: - /* 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. */ - + const wchar_t *_wgetopt_initialize(const wchar_t *optstring); + int _wgetopt_internal(int argc, wchar_t **argv, const wchar_t *optstring, + const struct woption *longopts, int *longind, int long_only); + + public: + // 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. 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'. - - 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. - - Otherwise, `woptind' communicates from one call to the next - how much of ARGV has been scanned so far. */ - - /* XXX 1003.2 says this must be 1 before any call. */ + + // 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. + // + // 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. + + // XXX 1003.2 says this must be 1 before any call. int woptind; - - - /* The next char to be scanned in the option-element - in which the last option character we returned was found. - This allows us to pick up the scan where we left off. - - If this is zero, or a null string, it means resume the scan - by advancing to the next ARGV-element. */ - + + // The next char to be scanned in the option-element in which the last option character we + // returned was found. This allows us to pick up the scan where we left off. + // + // If this is zero, or a null string, it means resume the scan by advancing to the next + // ARGV-element. wchar_t *nextchar; - - /* Callers store zero here to inhibit the error message - for unrecognized options. */ - + + // Callers store zero here to inhibit the error message for unrecognized options. int wopterr; - - /* Set to an option character which was unrecognized. - This must be initialized on some systems to avoid linking in the - system's own getopt implementation. */ - + + // Set to an option character which was unrecognized. This must be initialized on some systems + // to avoid linking in the system's own getopt implementation. int woptopt; - - /* Describe how to deal with options that follow non-option ARGV-elements. - - If the caller did not specify anything, - the default is PERMUTE. - - REQUIRE_ORDER means don't recognize them as options; - stop option processing when the first non-option is seen. - This is what Unix does. - This mode of operation is selected by using `+' as the first - character of the list of option characters. - - PERMUTE is the default. We permute the contents of ARGV as we scan, - so that eventually all the non-options are at the end. This allows options - to be given in any order, even with programs that were not written to - expect this. - - RETURN_IN_ORDER is an option available to programs that were written - to expect options and other ARGV-elements in any order and that care about - the ordering of the two. We describe each non-option ARGV-element - as if it were the argument of an option with character code 1. - Using `-' as the first character of the list of option characters - selects this mode of operation. - - The special argument `--' forces an end of option-scanning regardless - of the value of `ordering'. In the case of RETURN_IN_ORDER, only - `--' can cause `getopt' to return EOF with `woptind' != ARGC. */ - - enum - { - REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER - } ordering; - - /* Handle permutation of arguments. */ - - /* Describe the part of ARGV that contains non-options that have - been skipped. `first_nonopt' is the index in ARGV of the first of them; - `last_nonopt' is the index after the last of them. */ - + + // Describe how to deal with options that follow non-option ARGV-elements. + // + // If the caller did not specify anything, the default is PERMUTE. + // + // REQUIRE_ORDER means don't recognize them as options; stop option processing when the first + // non-option is seen. This is what Unix does. This mode of operation is selected by using `+' + // as the first character of the list of option characters. + // + // PERMUTE is the default. We permute the contents of ARGV as we scan, so that eventually all + // the non-options are at the end. This allows options to be given in any order, even with + // programs that were not written to expect this. + // + // RETURN_IN_ORDER is an option available to programs that were written to expect options and + // other ARGV-elements in any order and that care about the ordering of the two. We describe + // each non-option ARGV-element as if it were the argument of an option with character code 1. + // Using `-' as the first character of the list of option characters selects this mode of + // operation. + // + // The special argument `--' forces an end of option-scanning regardless of the value of + // `ordering'. In the case of RETURN_IN_ORDER, only `--' can cause `getopt' to return EOF with + // `woptind' != ARGC. + enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; + + // Handle permutation of arguments. + + // Describe the part of ARGV that contains non-options that have been skipped. `first_nonopt' + // is the index in ARGV of the first of them; `last_nonopt' is the index after the last of them. int first_nonopt; int last_nonopt; - - - wgetopter_t() : woptarg(NULL), woptind(0), nextchar(0), wopterr(0), woptopt('?'), ordering(), first_nonopt(0), last_nonopt(0) - { - } - - int wgetopt_long(int argc, wchar_t **argv, const wchar_t *options, const struct woption *long_options, int *opt_index); - int wgetopt_long_only(int argc, wchar_t **argv, const wchar_t *options, const struct woption *long_options, int *opt_index); + + wgetopter_t() + : woptarg(NULL), + woptind(0), + nextchar(0), + wopterr(0), + woptopt('?'), + ordering(), + first_nonopt(0), + last_nonopt(0) {} + + int wgetopt_long(int argc, wchar_t **argv, const wchar_t *options, + const struct woption *long_options, int *opt_index); + int wgetopt_long_only(int argc, wchar_t **argv, const wchar_t *options, + const struct woption *long_options, int *opt_index); }; -/** 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. - - 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. */ - -struct woption -{ - /** - long name for switch - */ +/// 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. +/// +/// 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. +struct woption { + /// Long name for switch. const wchar_t *name; - /** - 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. - */ + /// 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; - - /** - If non-null, the flag whose value should be set if this switch is encountered - */ + /// If non-null, the flag whose value should be set if this switch is encountered. int *flag; - - /** - If \c flag is non-null, this is the value that flag will be set - to. Otherwise, this is the return-value of the function call. - */ + /// If \c flag is non-null, this is the value that flag will be set to. Otherwise, this is the + /// return-value of the function call. int val; }; -/* 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 -*/ -#define no_argument 0 -/** - Specifies that a switch requires an argument -*/ -#define required_argument 1 -/** - Specifies that a switch accepts an optional argument -*/ -#define optional_argument 2 +/// Specifies that a switch does not accept an argument. +#define no_argument 0 +/// Specifies that a switch requires an argument. +#define required_argument 1 +/// Specifies that a switch accepts an optional argument. +#define optional_argument 2 #endif /* FISH_WGETOPT_H */ diff --git a/src/wildcard.cpp b/src/wildcard.cpp index 471453f62..1a09d63e9 100644 --- a/src/wildcard.cpp +++ b/src/wildcard.cpp @@ -1,113 +1,74 @@ -/** \file wildcard.c - -Fish needs it's own globbing implementation to support -tab-expansion of globbed parameters. Also provides recursive -wildcards using **. -*/ -#include -#include -#include +// Fish needs it's own globbing implementation to support tab-expansion of globbed parameters. Also +// provides recursive wildcards using **. +#include #include #include +#include +#include +#include +#include +#include +#include #include -#include #include #include -#include -#include -#include -#include "fallback.h" // IWYU pragma: keep -#include "wutil.h" // IWYU pragma: keep #include "common.h" -#include "wildcard.h" #include "complete.h" -#include "reader.h" #include "expand.h" +#include "fallback.h" // IWYU pragma: keep +#include "reader.h" +#include "wildcard.h" +#include "wutil.h" // IWYU pragma: keep -/** - Description for generic executable -*/ -#define COMPLETE_EXEC_DESC _( L"Executable" ) -/** - Description for link to executable -*/ -#define COMPLETE_EXEC_LINK_DESC _( L"Executable link" ) +/// Description for generic executable. +#define COMPLETE_EXEC_DESC _(L"Executable") +/// Description for link to executable. +#define COMPLETE_EXEC_LINK_DESC _(L"Executable link") +/// Description for regular file. +#define COMPLETE_FILE_DESC _(L"File") +/// Description for character device. +#define COMPLETE_CHAR_DESC _(L"Character device") +/// Description for block device. +#define COMPLETE_BLOCK_DESC _(L"Block device") +/// Description for fifo buffer. +#define COMPLETE_FIFO_DESC _(L"Fifo") +/// Description for symlink. +#define COMPLETE_SYMLINK_DESC _(L"Symbolic link") +/// Description for symlink. +#define COMPLETE_DIRECTORY_SYMLINK_DESC _(L"Symbolic link to directory") +/// Description for Rotten symlink. +#define COMPLETE_ROTTEN_SYMLINK_DESC _(L"Rotten symbolic link") +/// Description for symlink loop. +#define COMPLETE_LOOP_SYMLINK_DESC _(L"Symbolic link loop") +/// Description for socket files. +#define COMPLETE_SOCKET_DESC _(L"Socket") +/// Description for directories. +#define COMPLETE_DIRECTORY_DESC _(L"Directory") -/** - Description for regular file -*/ -#define COMPLETE_FILE_DESC _( L"File" ) -/** - Description for character device -*/ -#define COMPLETE_CHAR_DESC _( L"Character device" ) -/** - Description for block device -*/ -#define COMPLETE_BLOCK_DESC _( L"Block device" ) -/** - Description for fifo buffer -*/ -#define COMPLETE_FIFO_DESC _( L"Fifo" ) -/** - Description for symlink -*/ -#define COMPLETE_SYMLINK_DESC _( L"Symbolic link" ) -/** - Description for symlink -*/ -#define COMPLETE_DIRECTORY_SYMLINK_DESC _( L"Symbolic link to directory" ) -/** - Description for Rotten symlink -*/ -#define COMPLETE_ROTTEN_SYMLINK_DESC _( L"Rotten symbolic link" ) -/** - Description for symlink loop -*/ -#define COMPLETE_LOOP_SYMLINK_DESC _( L"Symbolic link loop" ) -/** - Description for socket files -*/ -#define COMPLETE_SOCKET_DESC _( L"Socket" ) -/** - Description for directories -*/ -#define COMPLETE_DIRECTORY_DESC _( L"Directory" ) - -/* Finds an internal (ANY_STRING, etc.) style wildcard, or wcstring::npos */ -static size_t wildcard_find(const wchar_t *wc) -{ - for (size_t i=0; wc[i] != L'\0'; i++) - { - if (wc[i] == ANY_CHAR || wc[i] == ANY_STRING || wc[i] == ANY_STRING_RECURSIVE) - { +/// Finds an internal (ANY_STRING, etc.) style wildcard, or wcstring::npos. +static size_t wildcard_find(const wchar_t *wc) { + for (size_t i = 0; wc[i] != L'\0'; i++) { + if (wc[i] == ANY_CHAR || wc[i] == ANY_STRING || wc[i] == ANY_STRING_RECURSIVE) { return i; } } return wcstring::npos; } -// Implementation of wildcard_has. Needs to take the length to handle embedded nulls (#1631) -static bool wildcard_has_impl(const wchar_t *str, size_t len, bool internal) -{ +/// Implementation of wildcard_has. Needs to take the length to handle embedded nulls (issue #1631). +static bool wildcard_has_impl(const wchar_t *str, size_t len, bool internal) { assert(str != NULL); const wchar_t *end = str + len; - if (internal) - { - for (; str < end; str++) - { + if (internal) { + for (; str < end; str++) { if ((*str == ANY_CHAR) || (*str == ANY_STRING) || (*str == ANY_STRING_RECURSIVE)) return true; } - } - else - { - wchar_t prev=0; - for (; str < end; str++) - { - if (((*str == L'*') || (*str == L'?')) && (prev != L'\\')) - return true; + } else { + wchar_t prev = 0; + for (; str < end; str++) { + if (((*str == L'*') || (*str == L'?')) && (prev != L'\\')) return true; prev = *str; } } @@ -115,141 +76,107 @@ static bool wildcard_has_impl(const wchar_t *str, size_t len, bool internal) return false; } -bool wildcard_has(const wchar_t *str, bool internal) -{ +bool wildcard_has(const wchar_t *str, bool internal) { assert(str != NULL); return wildcard_has_impl(str, wcslen(str), internal); } -bool wildcard_has(const wcstring &str, bool internal) -{ +bool wildcard_has(const wcstring &str, bool internal) { return wildcard_has_impl(str.data(), str.size(), internal); } - -/** - Check whether the string str matches the wildcard string wc. - - \param str String to be matched. - \param wc The wildcard. - \param is_first Whether files beginning with dots should not be matched against wildcards. -*/ -static enum fuzzy_match_type_t wildcard_match_internal(const wchar_t *str, const wchar_t *wc, bool leading_dots_fail_to_match, bool is_first) -{ - if (*str == 0 && *wc==0) - { - /* We're done */ - return fuzzy_match_exact; +/// Check whether the string str matches the wildcard string wc. +/// +/// \param str String to be matched. +/// \param wc The wildcard. +/// \param is_first Whether files beginning with dots should not be matched against wildcards. +static enum fuzzy_match_type_t wildcard_match_internal(const wchar_t *str, const wchar_t *wc, + bool leading_dots_fail_to_match, + bool is_first) { + if (*str == 0 && *wc == 0) { + return fuzzy_match_exact; // we're done } - /* Hackish fix for #270 . Prevent wildcards from matching . or .., but we must still allow literal matches. */ - if (leading_dots_fail_to_match && is_first && contains(str, L".", L"..")) - { - /* The string is '.' or '..'. Return true if the wildcard exactly matches. */ + // Hackish fix for issue #270. Prevent wildcards from matching . or .., but we must still allow + // literal matches. + if (leading_dots_fail_to_match && is_first && contains(str, L".", L"..")) { + // The string is '.' or '..'. Return true if the wildcard exactly matches. return wcscmp(str, wc) ? fuzzy_match_none : fuzzy_match_exact; } - - if (*wc == ANY_STRING || *wc == ANY_STRING_RECURSIVE) - { - /* Ignore hidden file */ - if (leading_dots_fail_to_match && is_first && *str == L'.') - { + + if (*wc == ANY_STRING || *wc == ANY_STRING_RECURSIVE) { + // Ignore hidden file + if (leading_dots_fail_to_match && is_first && *str == L'.') { return fuzzy_match_none; } - - /* Common case of * at the end. In that case we can early out since we know it will match. */ - if (wc[1] == L'\0') - { + + // Common case of * at the end. In that case we can early out since we know it will match. + if (wc[1] == L'\0') { return fuzzy_match_exact; } - /* Try all submatches */ - do - { - enum fuzzy_match_type_t subresult = wildcard_match_internal(str, wc+1, leading_dots_fail_to_match, false); - if (subresult != fuzzy_match_none) - { + // Try all submatches. + do { + enum fuzzy_match_type_t subresult = + wildcard_match_internal(str, wc + 1, leading_dots_fail_to_match, false); + if (subresult != fuzzy_match_none) { return subresult; } } while (*str++ != 0); return fuzzy_match_none; - } - 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. - */ + } 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 fuzzy_match_none; - } - else if (*wc == ANY_CHAR) - { - if (is_first && *str == L'.') - { + } else if (*wc == ANY_CHAR) { + if (is_first && *str == L'.') { return fuzzy_match_none; } - return wildcard_match_internal(str+1, wc+1, leading_dots_fail_to_match, false); - } - else if (*wc == *str) - { - return wildcard_match_internal(str+1, wc+1, leading_dots_fail_to_match, false); + return wildcard_match_internal(str + 1, wc + 1, leading_dots_fail_to_match, false); + } else if (*wc == *str) { + return wildcard_match_internal(str + 1, wc + 1, leading_dots_fail_to_match, false); } return fuzzy_match_none; } - -/* This does something horrible refactored from an even more horrible function */ -static wcstring resolve_description(wcstring *completion, const wchar_t *explicit_desc, wcstring(*desc_func)(const wcstring &)) -{ +// This does something horrible refactored from an even more horrible function. +static wcstring resolve_description(wcstring *completion, const wchar_t *explicit_desc, + wcstring (*desc_func)(const wcstring &)) { size_t complete_sep_loc = completion->find(PROG_COMPLETE_SEP); - if (complete_sep_loc != wcstring::npos) - { - /* This completion has an embedded description, do not use the generic description */ + if (complete_sep_loc != wcstring::npos) { + // This completion has an embedded description, do not use the generic description. const wcstring description = completion->substr(complete_sep_loc + 1); completion->resize(complete_sep_loc); return description; - } - else - { + } else { const wcstring func_result = (desc_func ? desc_func(*completion) : wcstring()); - if (! func_result.empty()) - { + if (!func_result.empty()) { return func_result; - } - else - { + } else { return explicit_desc ? explicit_desc : L""; } } } -/* A transient parameter pack needed by wildcard_complete. */ -struct wc_complete_pack_t -{ - const wcstring &orig; // the original string, transient - const wchar_t *desc; // literal description - wcstring(*desc_func)(const wcstring &); // function for generating descriptions +// A transient parameter pack needed by wildcard_complete. +struct wc_complete_pack_t { + const wcstring &orig; // the original string, transient + const wchar_t *desc; // literal description + wcstring (*desc_func)(const wcstring &); // function for generating descriptions expand_flags_t expand_flags; - wc_complete_pack_t(const wcstring &str, const wchar_t *des, wcstring(*df)(const wcstring &), expand_flags_t fl) : - orig(str), - desc(des), - desc_func(df), - expand_flags(fl) - {} + wc_complete_pack_t(const wcstring &str, const wchar_t *des, wcstring (*df)(const wcstring &), + expand_flags_t fl) + : orig(str), desc(des), desc_func(df), expand_flags(fl) {} }; -/* Weirdly specific and non-reusable helper function that makes its one call site much clearer */ -static bool has_prefix_match(const std::vector *comps, size_t first) -{ - if (comps != NULL) - { +// Weirdly specific and non-reusable helper function that makes its one call site much clearer. +static bool has_prefix_match(const std::vector *comps, size_t first) { + if (comps != NULL) { const size_t after_count = comps->size(); - for (size_t j = first; j < after_count; j++) - { - if (comps->at(j).match.type <= fuzzy_match_prefix) - { + for (size_t j = first; j < after_count; j++) { + if (comps->at(j).match.type <= fuzzy_match_prefix) { return true; } } @@ -257,136 +184,116 @@ static bool has_prefix_match(const std::vector *comps, size_t firs return false; } -/** - Matches the string against the wildcard, and if the wildcard is a - possible completion of the string, the remainder of the string is - inserted into the out vector. - - We ignore ANY_STRING_RECURSIVE here. The consequence is that you cannot - tab complete ** wildcards. This is historic behavior. - */ -static bool wildcard_complete_internal(const wchar_t *str, - const wchar_t *wc, - const wc_complete_pack_t ¶ms, - complete_flags_t flags, - std::vector *out, - bool is_first_call = false) -{ +/// Matches the string against the wildcard, and if the wildcard is a possible completion of the +/// string, the remainder of the string is inserted into the out vector. +/// +/// We ignore ANY_STRING_RECURSIVE here. The consequence is that you cannot tab complete ** +/// wildcards. This is historic behavior. +static bool wildcard_complete_internal(const wchar_t *str, const wchar_t *wc, + const wc_complete_pack_t ¶ms, complete_flags_t flags, + std::vector *out, bool is_first_call = false) { assert(str != NULL); assert(wc != NULL); - - /* Maybe early out for hidden files. We require that the wildcard match these exactly (i.e. a dot); ANY_STRING not allowed */ - if (is_first_call && str[0] == L'.' && wc[0] != L'.') - { + + // Maybe early out for hidden files. We require that the wildcard match these exactly (i.e. a + // dot); ANY_STRING not allowed. + if (is_first_call && str[0] == L'.' && wc[0] != L'.') { return false; } - - /* Locate the next wildcard character position, e.g. ANY_CHAR or ANY_STRING */ + + // Locate the next wildcard character position, e.g. ANY_CHAR or ANY_STRING. const size_t next_wc_char_pos = wildcard_find(wc); - - /* Maybe we have no more wildcards at all. This includes the empty string. */ - if (next_wc_char_pos == wcstring::npos) - { + + // Maybe we have no more wildcards at all. This includes the empty string. + if (next_wc_char_pos == wcstring::npos) { string_fuzzy_match_t match = string_fuzzy_match_string(wc, str); - - /* If we're allowing fuzzy match, any match is OK. Otherwise we require a prefix match. */ + + // If we're allowing fuzzy match, any match is OK. Otherwise we require a prefix match. bool match_acceptable; - if (params.expand_flags & EXPAND_FUZZY_MATCH) - { + if (params.expand_flags & EXPAND_FUZZY_MATCH) { match_acceptable = match.type != fuzzy_match_none; - } - else - { + } else { match_acceptable = match_type_shares_prefix(match.type); } - - if (match_acceptable && out != NULL) - { - /* Wildcard complete */ - bool full_replacement = match_type_requires_full_replacement(match.type) || (flags & COMPLETE_REPLACES_TOKEN); - - /* If we are not replacing the token, be careful to only store the part of the string after the wildcard */ + + if (match_acceptable && out != NULL) { + // Wildcard complete. + bool full_replacement = match_type_requires_full_replacement(match.type) || + (flags & COMPLETE_REPLACES_TOKEN); + + // If we are not replacing the token, be careful to only store the part of the string + // after the wildcard. assert(!full_replacement || wcslen(wc) <= wcslen(str)); wcstring out_completion = full_replacement ? params.orig : str + wcslen(wc); wcstring out_desc = resolve_description(&out_completion, params.desc, params.desc_func); - - /* Note: out_completion may be empty if the completion really is empty, e.g. tab-completing 'foo' when a file 'foo' exists. */ + + // Note: out_completion may be empty if the completion really is empty, e.g. + // tab-completing 'foo' when a file 'foo' exists. complete_flags_t local_flags = flags | (full_replacement ? COMPLETE_REPLACES_TOKEN : 0); append_completion(out, out_completion, out_desc, local_flags, match); } return match_acceptable; - } - else if (next_wc_char_pos > 0) - { - /* Here we have a non-wildcard prefix. Note that we don't do fuzzy matching for stuff before a wildcard, so just do case comparison and then recurse. */ - if (wcsncmp(str, wc, next_wc_char_pos) == 0) - { - // Normal match - return wildcard_complete_internal(str + next_wc_char_pos, wc + next_wc_char_pos, params, flags, out); - } - else if (wcsncasecmp(str, wc, next_wc_char_pos) == 0) - { - // Case insensitive match - return wildcard_complete_internal(str + next_wc_char_pos, wc + next_wc_char_pos, params, flags | COMPLETE_REPLACES_TOKEN, out); - } - else - { - // No match + } else if (next_wc_char_pos > 0) { + // Here we have a non-wildcard prefix. Note that we don't do fuzzy matching for stuff before + // a wildcard, so just do case comparison and then recurse. + if (wcsncmp(str, wc, next_wc_char_pos) == 0) { + // Normal match. + return wildcard_complete_internal(str + next_wc_char_pos, wc + next_wc_char_pos, params, + flags, out); + } else if (wcsncasecmp(str, wc, next_wc_char_pos) == 0) { + // Case insensitive match. + return wildcard_complete_internal(str + next_wc_char_pos, wc + next_wc_char_pos, params, + flags | COMPLETE_REPLACES_TOKEN, out); + } else { + // No match. return false; } assert(0 && "Unreachable code reached"); - } - else - { - /* Our first character is a wildcard. */ + } else { + // Our first character is a wildcard. assert(next_wc_char_pos == 0); - switch (wc[0]) - { - case ANY_CHAR: - { - if (str[0] == L'\0') - { + switch (wc[0]) { + case ANY_CHAR: { + if (str[0] == L'\0') { return false; - } - else - { + } else { return wildcard_complete_internal(str + 1, wc + 1, params, flags, out); } break; } - - case ANY_STRING: - { - /* Hackish. If this is the last character of the wildcard, then just complete with the empty string. This fixes cases like "f*" -> "f*o" */ - if (wc[1] == L'\0') - { + + case ANY_STRING: { + // Hackish. If this is the last character of the wildcard, then just complete with + // the empty string. This fixes cases like "f*" -> "f*o". + if (wc[1] == L'\0') { return wildcard_complete_internal(L"", L"", params, flags, out); } - - /* Try all submatches. #929: if the recursive call gives us a prefix match, just stop. This is sloppy - what we really want to do is say, once we've seen a match of a particular type, ignore all matches of that type further down the string, such that the wildcard produces the "minimal match.". */ + + // Try all submatches. Issue #929: if the recursive call gives us a prefix match, + // just stop. This is sloppy - what we really want to do is say, once we've seen a + // match of a particular type, ignore all matches of that type further down the + // string, such that the wildcard produces the "minimal match.". bool has_match = false; - for (size_t i=0; str[i] != L'\0'; i++) - { + for (size_t i = 0; str[i] != L'\0'; i++) { const size_t before_count = out ? out->size() : 0; - if (wildcard_complete_internal(str + i, wc + 1, params, flags, out)) - { - /* We found a match */ + if (wildcard_complete_internal(str + i, wc + 1, params, flags, out)) { + // We found a match. has_match = true; - - /* If out is NULL, we don't care about the actual matches. If out is not NULL but we have a prefix match, stop there. */ - if (out == NULL || has_prefix_match(out, before_count)) - { + + // If out is NULL, we don't care about the actual matches. If out is not + // NULL but we have a prefix match, stop there. + if (out == NULL || has_prefix_match(out, before_count)) { break; } } } return has_match; } - + case ANY_STRING_RECURSIVE: - /* We don't even try with this one */ + // We don't even try with this one. return false; - + default: assert(0 && "Unreachable code reached"); return false; @@ -395,160 +302,99 @@ static bool wildcard_complete_internal(const wchar_t *str, assert(0 && "Unreachable code reached"); } -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 expand_flags, - complete_flags_t flags) -{ - // Note out may be NULL +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 expand_flags, complete_flags_t flags) { + // Note out may be NULL. assert(wc != NULL); wc_complete_pack_t params(str, desc, desc_func, expand_flags); return wildcard_complete_internal(str.c_str(), wc, params, flags, out, true /* first call */); } - -bool wildcard_match(const wcstring &str, const wcstring &wc, bool leading_dots_fail_to_match) -{ - enum fuzzy_match_type_t match = wildcard_match_internal(str.c_str(), wc.c_str(), leading_dots_fail_to_match, true /* first */); +bool wildcard_match(const wcstring &str, const wcstring &wc, bool leading_dots_fail_to_match) { + enum fuzzy_match_type_t match = wildcard_match_internal( + str.c_str(), wc.c_str(), leading_dots_fail_to_match, true /* first */); return match != fuzzy_match_none; } -/** - Obtain a description string for the file specified by the filename. - - The returned value is a string constant and should not be free'd. - - \param filename The file for which to find a description string - \param lstat_res The result of calling lstat on the file - \param lbuf The struct buf output of calling lstat on the file - \param stat_res The result of calling stat on the file - \param buf The struct buf output of calling stat on the file - \param err The errno value after a failed stat call on the file. - */ - -static wcstring file_get_desc(const wcstring &filename, - int lstat_res, - const struct stat &lbuf, - int stat_res, - const struct stat &buf, - int err) -{ - - if (!lstat_res) - { - if (S_ISLNK(lbuf.st_mode)) - { - if (!stat_res) - { - if (S_ISDIR(buf.st_mode)) - { +/// Obtain a description string for the file specified by the filename. +/// +/// The returned value is a string constant and should not be free'd. +/// +/// \param filename The file for which to find a description string +/// \param lstat_res The result of calling lstat on the file +/// \param lbuf The struct buf output of calling lstat on the file +/// \param stat_res The result of calling stat on the file +/// \param buf The struct buf output of calling stat on the file +/// \param err The errno value after a failed stat call on the file. +static wcstring file_get_desc(const wcstring &filename, int lstat_res, const struct stat &lbuf, + int stat_res, const struct stat &buf, int err) { + if (!lstat_res) { + if (S_ISLNK(lbuf.st_mode)) { + if (!stat_res) { + if (S_ISDIR(buf.st_mode)) { return COMPLETE_DIRECTORY_SYMLINK_DESC; - } - else - { - - if (buf.st_mode & (S_IXUSR | S_IXGRP | 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. - */ + } else { + if (buf.st_mode & (S_IXUSR | S_IXGRP | 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: - { + + } else { + switch (err) { + case ENOENT: { return COMPLETE_ROTTEN_SYMLINK_DESC; } - - case ELOOP: - { + 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. - */ + // 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)) - { + + } else if (S_ISCHR(buf.st_mode)) { return COMPLETE_CHAR_DESC; - } - else if (S_ISBLK(buf.st_mode)) - { + } else if (S_ISBLK(buf.st_mode)) { return COMPLETE_BLOCK_DESC; - } - else if (S_ISFIFO(buf.st_mode)) - { + } else if (S_ISFIFO(buf.st_mode)) { return COMPLETE_FIFO_DESC; - } - else if (S_ISSOCK(buf.st_mode)) - { + } else if (S_ISSOCK(buf.st_mode)) { return COMPLETE_SOCKET_DESC; - } - else if (S_ISDIR(buf.st_mode)) - { + } else if (S_ISDIR(buf.st_mode)) { return COMPLETE_DIRECTORY_DESC; - } - else - { - if (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXGRP)) - { - - 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. - */ + } else { + if (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXGRP)) { + 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_FILE_DESC ; + + return COMPLETE_FILE_DESC; } -/** Test if the given file is an executable (if EXECUTABLES_ONLY) or directory (if DIRECTORIES_ONLY). - If it matches, call wildcard_complete() with some description that we make up. - Note that the filename came from a readdir() call, so we know it exists. - */ -static bool wildcard_test_flags_then_complete(const wcstring &filepath, - const wcstring &filename, - const wchar_t *wc, - expand_flags_t expand_flags, - std::vector *out) -{ - /* Check if it will match before stat() */ - if (! wildcard_complete(filename, wc, NULL, NULL, NULL, expand_flags, 0)) - { +/// Test if the given file is an executable (if EXECUTABLES_ONLY) or directory (if +/// DIRECTORIES_ONLY). If it matches, call wildcard_complete() with some description that we make +/// up. Note that the filename came from a readdir() call, so we know it exists. +static bool wildcard_test_flags_then_complete(const wcstring &filepath, const wcstring &filename, + const wchar_t *wc, expand_flags_t expand_flags, + std::vector *out) { + // Check if it will match before stat(). + if (!wildcard_complete(filename, wc, NULL, NULL, NULL, expand_flags, 0)) { return false; } @@ -556,199 +402,160 @@ static bool wildcard_test_flags_then_complete(const wcstring &filepath, int stat_res = -1; int stat_errno = 0; int lstat_res = lwstat(filepath, &lstat_buf); - if (lstat_res < 0) - { - /* lstat failed */ - } - else - { - if (S_ISLNK(lstat_buf.st_mode)) - { + if (lstat_res < 0) { + // lstat failed. + } else { + if (S_ISLNK(lstat_buf.st_mode)) { stat_res = wstat(filepath, &stat_buf); - - if (stat_res < 0) - { - /* - In order to differentiate between e.g. rotten symlinks - and symlink loops, we also need to know the error status of wstat. - */ + + if (stat_res < 0) { + // 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 - { + } else { stat_buf = lstat_buf; stat_res = lstat_res; } } - + const long long file_size = stat_res == 0 ? stat_buf.st_size : 0; const bool is_directory = stat_res == 0 && S_ISDIR(stat_buf.st_mode); const bool is_executable = stat_res == 0 && S_ISREG(stat_buf.st_mode); - - if (expand_flags & DIRECTORIES_ONLY) - { - if (!is_directory) - { + + if (expand_flags & DIRECTORIES_ONLY) { + if (!is_directory) { return false; } } - if (expand_flags & EXECUTABLES_ONLY) - { - if (!is_executable || waccess(filepath, X_OK) != 0) - { + if (expand_flags & EXECUTABLES_ONLY) { + if (!is_executable || waccess(filepath, X_OK) != 0) { return false; } } - - /* Compute the description */ + + // Compute the description. wcstring desc; - if (!(expand_flags & EXPAND_NO_DESCRIPTIONS)) - { + if (!(expand_flags & EXPAND_NO_DESCRIPTIONS)) { desc = file_get_desc(filepath, lstat_res, lstat_buf, stat_res, stat_buf, stat_errno); - - if (file_size >= 0) - { - if (!desc.empty()) - desc.append(L", "); + + if (file_size >= 0) { + if (!desc.empty()) desc.append(L", "); desc.append(format_size(file_size)); } } - - /* Append a / if this is a directory. Note this requirement may be the only reason we have to call stat() in some cases. */ - if (is_directory) - { - return wildcard_complete(filename + L'/', wc, desc.c_str(), NULL, out, expand_flags, COMPLETE_NO_SPACE); - } - else - { + + // Append a / if this is a directory. Note this requirement may be the only reason we have to + // call stat() in some cases. + if (is_directory) { + return wildcard_complete(filename + L'/', wc, desc.c_str(), NULL, out, expand_flags, + COMPLETE_NO_SPACE); + } else { return wildcard_complete(filename, wc, desc.c_str(), NULL, out, expand_flags, 0); } } -class wildcard_expander_t -{ - /* Prefix, i.e. effective working directory */ +class wildcard_expander_t { + // Prefix, i.e. effective working directory. const wcstring prefix; - - /* The original base we are expanding */ + // The original base we are expanding. const wcstring original_base; - - /* Original wildcard we are expanding. */ - const wchar_t * const original_wildcard; - - /* the set of items we have resolved, used to efficiently avoid duplication */ + // Original wildcard we are expanding. + const wchar_t *const original_wildcard; + // The set of items we have resolved, used to efficiently avoid duplication. std::set completion_set; - - /* the set of file IDs we have visited, used to avoid symlink loops */ + // The set of file IDs we have visited, used to avoid symlink loops. std::set visited_files; - - /* flags controlling expansion */ + // Flags controlling expansion. const expand_flags_t flags; - - /* resolved items get inserted into here. This is transient of course. */ + // Resolved items get inserted into here. This is transient of course. std::vector *resolved_completions; - - /* whether we have been interrupted */ + // Whether we have been interrupted. bool did_interrupt; - - /* whether we have successfully added any completions */ + // Whether we have successfully added any completions. bool did_add; - - /* We are a trailing slash - expand at the end */ + + /// We are a trailing slash - expand at the end. void expand_trailing_slash(const wcstring &base_dir); - - /* Given a directory base_dir, which is opened as base_dir_fp, expand an intermediate segment of the wildcard. - Treat ANY_STRING_RECURSIVE as ANY_STRING. - wc_segment is the wildcard segment for this directory - wc_remainder is the wildcard for subdirectories - */ - void expand_intermediate_segment(const wcstring &base_dir, DIR *base_dir_fp, const wcstring &wc_segment, const wchar_t *wc_remainder); - - /* Given a directory base_dir, which is opened as base_dir_fp, expand an intermediate literal segment. - Use a fuzzy matching algorithm. - */ - void expand_literal_intermediate_segment_with_fuzz(const wcstring &base_dir, DIR *base_dir_fp, const wcstring &wc_segment, const wchar_t *wc_remainder); - - /* Given a directory base_dir, which is opened as base_dir_fp, expand the last segment of the wildcard. - Treat ANY_STRING_RECURSIVE as ANY_STRING. - wc is the wildcard segment to use for matching - wc_remainder is the wildcard for subdirectories - */ + + /// Given a directory base_dir, which is opened as base_dir_fp, expand an intermediate segment + /// of the wildcard. Treat ANY_STRING_RECURSIVE as ANY_STRING. wc_segment is the wildcard + /// segment for this directory wc_remainder is the wildcard for subdirectories + void expand_intermediate_segment(const wcstring &base_dir, DIR *base_dir_fp, + const wcstring &wc_segment, const wchar_t *wc_remainder); + + /// Given a directory base_dir, which is opened as base_dir_fp, expand an intermediate literal + /// segment. Use a fuzzy matching algorithm. + void expand_literal_intermediate_segment_with_fuzz(const wcstring &base_dir, DIR *base_dir_fp, + const wcstring &wc_segment, + const wchar_t *wc_remainder); + + /// Given a directory base_dir, which is opened as base_dir_fp, expand the last segment of the + /// wildcard. Treat ANY_STRING_RECURSIVE as ANY_STRING. wc is the wildcard segment to use for + /// matching wc_remainder is the wildcard for subdirectories. void expand_last_segment(const wcstring &base_dir, DIR *base_dir_fp, const wcstring &wc); - - /* Indicate whether we should cancel wildcard expansion. This latches 'interrupt' */ - bool interrupted() - { - if (! did_interrupt) - { - did_interrupt = (is_main_thread() ? reader_interrupted() : reader_thread_job_is_stale()); + + /// Indicate whether we should cancel wildcard expansion. This latches 'interrupt'. + bool interrupted() { + if (!did_interrupt) { + did_interrupt = + (is_main_thread() ? reader_interrupted() : reader_thread_job_is_stale()); } return did_interrupt; } - - void add_expansion_result(const wcstring &result) - { - /* This function is only for the non-completions case */ - assert(! (this->flags & EXPAND_FOR_COMPLETIONS)); - if (this->completion_set.insert(result).second) - { + + void add_expansion_result(const wcstring &result) { + // This function is only for the non-completions case. + assert(!(this->flags & EXPAND_FOR_COMPLETIONS)); + if (this->completion_set.insert(result).second) { append_completion(this->resolved_completions, result); this->did_add = true; } } - - /* Given a start point as an absolute path, for any directory that has exactly one non-hidden entity in it which is itself a directory, return that. The result is a relative path. For example, if start_point is '/usr' we may return 'local/bin/'. - - The result does not have a leading slash, but does have a trailing slash if non-empty. */ - wcstring descend_unique_hierarchy(const wcstring &start_point) - { - assert(! start_point.empty() && start_point.at(0) == L'/'); - + + // Given a start point as an absolute path, for any directory that has exactly one non-hidden + // entity in it which is itself a directory, return that. The result is a relative path. For + // example, if start_point is '/usr' we may return 'local/bin/'. + // + // The result does not have a leading slash, but does have a trailing slash if non-empty. + wcstring descend_unique_hierarchy(const wcstring &start_point) { + assert(!start_point.empty() && start_point.at(0) == L'/'); + wcstring unique_hierarchy; wcstring abs_unique_hierarchy = start_point; - + bool stop_descent = false; DIR *dir; - while (!stop_descent && (dir = wopendir(abs_unique_hierarchy))) - { - /* We keep track of the single unique_entry entry. If we get more than one, it's not unique and we stop the descent. */ + while (!stop_descent && (dir = wopendir(abs_unique_hierarchy))) { + // We keep track of the single unique_entry entry. If we get more than one, it's not + // unique and we stop the descent. wcstring unique_entry; - + bool child_is_dir; wcstring child_entry; - while (wreaddir_resolving(dir, abs_unique_hierarchy, child_entry, &child_is_dir)) - { - if (child_entry.empty() || child_entry.at(0) == L'.') - { - /* Either hidden, or . and .. entries. Skip them. */ - continue; - } - else if (child_is_dir && unique_entry.empty()) - { - /* First candidate */ - unique_entry = child_entry; - } - else - { - /* We either have two or more candidates, or the child is not a directory. We're done. */ + while (wreaddir_resolving(dir, abs_unique_hierarchy, child_entry, &child_is_dir)) { + if (child_entry.empty() || child_entry.at(0) == L'.') { + continue; // either hidden, or . and .. entries -- skip them + } else if (child_is_dir && unique_entry.empty()) { + unique_entry = child_entry; // first candidate + } else { + // We either have two or more candidates, or the child is not a directory. We're + // done. stop_descent = true; break; } } - - /* We stop if we got two or more entries; also stop if we got zero. */ - if (unique_entry.empty()) - { + + // We stop if we got two or more entries; also stop if we got zero. + if (unique_entry.empty()) { stop_descent = true; } - - if (! stop_descent) - { - /* We have an entry in the unique hierarchy! */ + + if (!stop_descent) { + // We have an entry in the unique hierarchy! append_path_component(unique_hierarchy, unique_entry); unique_hierarchy.push_back(L'/'); - + append_path_component(abs_unique_hierarchy, unique_entry); abs_unique_hierarchy.push_back(L'/'); } @@ -757,121 +564,104 @@ class wildcard_expander_t return unique_hierarchy; } - - void try_add_completion_result(const wcstring &filepath, const wcstring &filename, const wcstring &wildcard) - { - /* This function is only for the completions case */ + void try_add_completion_result(const wcstring &filepath, const wcstring &filename, + const wcstring &wildcard) { + // This function is only for the completions case. assert(this->flags & EXPAND_FOR_COMPLETIONS); - + wcstring abs_path = this->prefix; append_path_component(abs_path, filepath); - + size_t before = this->resolved_completions->size(); - if (wildcard_test_flags_then_complete(abs_path, filename, wildcard.c_str(), this->flags, this->resolved_completions)) - { - /* Hack. We added this completion result based on the last component of the wildcard. - Prepend all prior components of the wildcard to each completion that replaces its token. */ + if (wildcard_test_flags_then_complete(abs_path, filename, wildcard.c_str(), this->flags, + this->resolved_completions)) { + // Hack. We added this completion result based on the last component of the wildcard. + // Prepend all prior components of the wildcard to each completion that replaces its + // token. size_t wc_len = wildcard.size(); size_t orig_wc_len = wcslen(this->original_wildcard); assert(wc_len <= orig_wc_len); const wcstring wc_base(this->original_wildcard, orig_wc_len - wc_len); - + size_t after = this->resolved_completions->size(); - for (size_t i=before; i < after; i++) - { + for (size_t i = before; i < after; i++) { completion_t &c = this->resolved_completions->at(i); c.prepend_token_prefix(wc_base); c.prepend_token_prefix(this->original_base); } - - /* Hack. Implement EXPAND_SPECIAL_FOR_CD by descending the deepest unique hierarchy we can, and then appending any components to each new result. */ - if (flags & EXPAND_SPECIAL_FOR_CD) - { + + // Hack. Implement EXPAND_SPECIAL_FOR_CD by descending the deepest unique hierarchy we + // can, and then appending any components to each new result. + if (flags & EXPAND_SPECIAL_FOR_CD) { wcstring unique_hierarchy = this->descend_unique_hierarchy(abs_path); - if (! unique_hierarchy.empty()) - { - for (size_t i=before; i < after; i++) - { + if (!unique_hierarchy.empty()) { + for (size_t i = before; i < after; i++) { completion_t &c = this->resolved_completions->at(i); c.completion.append(unique_hierarchy); } } } - + this->did_add = true; } } - - /* Helper to resolve using our prefix */ - DIR *open_dir(const wcstring &base_dir) const - { + + // Helper to resolve using our prefix. + DIR *open_dir(const wcstring &base_dir) const { wcstring path = this->prefix; append_path_component(path, base_dir); return wopendir(path); } - -public: - - wildcard_expander_t(const wcstring &pref, const wcstring &orig_base, const wchar_t *orig_wc, expand_flags_t f, std::vector *r) : - prefix(pref), - original_base(orig_base), - original_wildcard(orig_wc), - flags(f), - resolved_completions(r), - did_interrupt(false), - did_add(false) - { + + public: + wildcard_expander_t(const wcstring &pref, const wcstring &orig_base, const wchar_t *orig_wc, + expand_flags_t f, std::vector *r) + : prefix(pref), + original_base(orig_base), + original_wildcard(orig_wc), + flags(f), + resolved_completions(r), + did_interrupt(false), + did_add(false) { assert(resolved_completions != NULL); - - /* Insert initial completions into our set to avoid duplicates */ - for (std::vector::const_iterator iter = resolved_completions->begin(); iter != resolved_completions->end(); ++iter) - { + + // Insert initial completions into our set to avoid duplicates. + for (std::vector::const_iterator iter = resolved_completions->begin(); + iter != resolved_completions->end(); ++iter) { this->completion_set.insert(iter->completion); } } - - /* Do wildcard expansion. This is recursive. */ + + // Do wildcard expansion. This is recursive. void expand(const wcstring &base_dir, const wchar_t *wc); - - int status_code() const - { - if (this->did_interrupt) - { + + int status_code() const { + if (this->did_interrupt) { return -1; - } - else - { + } else { return this->did_add ? 1 : 0; } } }; -void wildcard_expander_t::expand_trailing_slash(const wcstring &base_dir) -{ - if (interrupted()) - { +void wildcard_expander_t::expand_trailing_slash(const wcstring &base_dir) { + if (interrupted()) { return; } - - if (! (flags & EXPAND_FOR_COMPLETIONS)) - { - /* Trailing slash and not accepting incomplete, e.g. `echo /tmp/`. Insert this file if it exists. */ - if (waccess(base_dir, F_OK) == 0) - { + + if (!(flags & EXPAND_FOR_COMPLETIONS)) { + // Trailing slash and not accepting incomplete, e.g. `echo /tmp/`. Insert this file if it + // exists. + if (waccess(base_dir, F_OK) == 0) { this->add_expansion_result(base_dir); } - } - else - { - /* Trailing slashes and accepting incomplete, e.g. `echo /tmp/`. Everything is added. */ + } else { + // Trailing slashes and accepting incomplete, e.g. `echo /tmp/`. Everything is added. DIR *dir = open_dir(base_dir); - if (dir) - { + if (dir) { wcstring next; - while (wreaddir(dir, next) && ! interrupted()) - { - if (! next.empty() && next.at(0) != L'.') - { + while (wreaddir(dir, next) && !interrupted()) { + if (!next.empty() && next.at(0) != L'.') { this->try_add_completion_result(base_dir + next, next, L""); } } @@ -880,239 +670,212 @@ void wildcard_expander_t::expand_trailing_slash(const wcstring &base_dir) } } -void wildcard_expander_t::expand_intermediate_segment(const wcstring &base_dir, DIR *base_dir_fp, const wcstring &wc_segment, const wchar_t *wc_remainder) -{ +void wildcard_expander_t::expand_intermediate_segment(const wcstring &base_dir, DIR *base_dir_fp, + const wcstring &wc_segment, + const wchar_t *wc_remainder) { wcstring name_str; - while (!interrupted() && wreaddir_for_dirs(base_dir_fp, &name_str)) - { - /* Note that it's critical we ignore leading dots here, else we may descend into . and .. */ - if (! wildcard_match(name_str, wc_segment, true)) - { - /* Doesn't match the wildcard for this segment, skip it */ + while (!interrupted() && wreaddir_for_dirs(base_dir_fp, &name_str)) { + // Note that it's critical we ignore leading dots here, else we may descend into . and .. + if (!wildcard_match(name_str, wc_segment, true)) { + // Doesn't match the wildcard for this segment, skip it. continue; } - + wcstring full_path = base_dir + name_str; struct stat buf; - if (0 != wstat(full_path, &buf) || !S_ISDIR(buf.st_mode)) - { - /* We either can't stat it, or we did but it's not a directory */ + if (0 != wstat(full_path, &buf) || !S_ISDIR(buf.st_mode)) { + // We either can't stat it, or we did but it's not a directory. continue; } const file_id_t file_id = file_id_t::file_id_from_stat(&buf); - if (!this->visited_files.insert(file_id).second) - { - /* Symlink loop! This directory was already visited, so skip it */ + if (!this->visited_files.insert(file_id).second) { + // Symlink loop! This directory was already visited, so skip it. continue; } - /* We made it through. Perform normal wildcard expansion on this new directory, starting at our tail_wc, which includes the ANY_STRING_RECURSIVE guy. */ + // We made it through. Perform normal wildcard expansion on this new directory, starting at + // our tail_wc, which includes the ANY_STRING_RECURSIVE guy. full_path.push_back(L'/'); this->expand(full_path, wc_remainder); - /* Now remove the visited file. This is for #2414: only directories "beneath" us should be considered visited. */ + // Now remove the visited file. This is for #2414: only directories "beneath" us should be + // considered visited. this->visited_files.erase(file_id); } } - -void wildcard_expander_t::expand_literal_intermediate_segment_with_fuzz(const wcstring &base_dir, DIR *base_dir_fp, const wcstring &wc_segment, const wchar_t *wc_remainder) -{ - // This only works with tab completions - // Ordinary wildcard expansion should never go fuzzy + +void wildcard_expander_t::expand_literal_intermediate_segment_with_fuzz( + const wcstring &base_dir, DIR *base_dir_fp, const wcstring &wc_segment, + const wchar_t *wc_remainder) { + // This only works with tab completions. Ordinary wildcard expansion should never go fuzzy. wcstring name_str; - while (!interrupted() && wreaddir_for_dirs(base_dir_fp, &name_str)) - { - /* Don't bother with . and .. */ - if (contains(name_str, L".", L"..")) - { + while (!interrupted() && wreaddir_for_dirs(base_dir_fp, &name_str)) { + // Don't bother with . and .. + if (contains(name_str, L".", L"..")) { continue; } - - // Skip cases that don't match or match exactly - // The match-exactly case was handled directly in expand() + + // Skip cases that don't match or match exactly. The match-exactly case was handled directly + // in expand(). const string_fuzzy_match_t match = string_fuzzy_match_string(wc_segment, name_str); - if (match.type == fuzzy_match_none || match.type == fuzzy_match_exact) - { + if (match.type == fuzzy_match_none || match.type == fuzzy_match_exact) { continue; } - + wcstring new_full_path = base_dir + name_str; new_full_path.push_back(L'/'); struct stat buf; - if (0 != wstat(new_full_path, &buf) || !S_ISDIR(buf.st_mode)) - { + if (0 != wstat(new_full_path, &buf) || !S_ISDIR(buf.st_mode)) { /* We either can't stat it, or we did but it's not a directory */ continue; } - - // Ok, this directory matches. Recurse to it. - // Then perform serious surgery on each result! - // Each result was computed with a prefix of original_wildcard - // We need to replace our segment of that with our name_str - // We also have to mark the completion as replacing and fuzzy + + // Ok, this directory matches. Recurse to it. Then perform serious surgery on each result! + // Each result was computed with a prefix of original_wildcard. We need to replace our + // segment of that with our name_str. We also have to mark the completion as replacing and + // fuzzy. const size_t before = this->resolved_completions->size(); - + this->expand(new_full_path, wc_remainder); const size_t after = this->resolved_completions->size(); - + assert(before <= after); - for (size_t i=before; i < after; i++) - { + for (size_t i = before; i < after; i++) { completion_t *c = &this->resolved_completions->at(i); - // Mark the completion as replacing - if (!(c->flags & COMPLETE_REPLACES_TOKEN)) - { + // Mark the completion as replacing. + if (!(c->flags & COMPLETE_REPLACES_TOKEN)) { c->flags |= COMPLETE_REPLACES_TOKEN; c->prepend_token_prefix(this->original_wildcard); c->prepend_token_prefix(this->original_base); } - // Ok, it's now replacing and is prefixed with the segment base, plus our original wildcard - // Replace our segment with name_str - // Our segment starts at the length of the original wildcard, minus what we have left to process, minus the length of our segment - // This logic is way too picky. Need to clean this up. - // One possibility is to send the "resolved wildcard" along with the actual wildcard + // Ok, it's now replacing and is prefixed with the segment base, plus our original + // wildcard. Replace our segment with name_str. Our segment starts at the length of the + // original wildcard, minus what we have left to process, minus the length of our + // segment. This logic is way too picky. Need to clean this up. One possibility is to + // send the "resolved wildcard" along with the actual wildcard. const size_t original_wildcard_len = wcslen(this->original_wildcard); const size_t wc_remainder_len = wcslen(wc_remainder); const size_t segment_len = wc_segment.length(); assert(c->completion.length() >= original_wildcard_len); - const size_t segment_start = original_wildcard_len + this->original_base.size() - wc_remainder_len - wc_segment.length() - 1; // -1 for the slash after our segment + const size_t segment_start = original_wildcard_len + this->original_base.size() - + wc_remainder_len - wc_segment.length() - + 1; // -1 for the slash after our segment assert(segment_start < original_wildcard_len); assert(c->completion.substr(segment_start, segment_len) == wc_segment); c->completion.replace(segment_start, segment_len, name_str); - - // And every match must be made at least as fuzzy as ours - if (match.compare(c->match) > 0) - { - // Our match is fuzzier + + // And every match must be made at least as fuzzy as ours. + if (match.compare(c->match) > 0) { + // Our match is fuzzier. c->match = match; } } } } -void wildcard_expander_t::expand_last_segment(const wcstring &base_dir, DIR *base_dir_fp, const wcstring &wc) -{ +void wildcard_expander_t::expand_last_segment(const wcstring &base_dir, DIR *base_dir_fp, + const wcstring &wc) { wcstring name_str; - while (wreaddir(base_dir_fp, name_str)) - { - if (flags & EXPAND_FOR_COMPLETIONS) - { + while (wreaddir(base_dir_fp, name_str)) { + if (flags & EXPAND_FOR_COMPLETIONS) { this->try_add_completion_result(base_dir + name_str, name_str, wc); - } - else - { - // Normal wildcard expansion, not for completions - if (wildcard_match(name_str, wc, true /* skip files with leading dots */)) - { + } else { + // Normal wildcard expansion, not for completions. + if (wildcard_match(name_str, wc, true /* skip files with leading dots */)) { this->add_expansion_result(base_dir + name_str); } } } } -/** - The real implementation of wildcard expansion is in this - function. Other functions are just wrappers around this one. - - This function traverses the relevant directory tree looking for - matches, and recurses when needed to handle wildcrards spanning - multiple components and recursive wildcards. - - Because this function calls itself recursively with substrings, - it's important that the parameters be raw pointers instead of wcstring, - which would be too expensive to construct for all substrings. - - Args: - base_dir: the "working directory" against which the wildcard is to be resolved - wc: the wildcard string itself, e.g. foo*bar/baz (where * is acutally ANY_CHAR) -*/ -void wildcard_expander_t::expand(const wcstring &base_dir, const wchar_t *wc) -{ +/// The real implementation of wildcard expansion is in this function. Other functions are just +/// wrappers around this one. +/// +/// This function traverses the relevant directory tree looking for matches, and recurses when +/// needed to handle wildcrards spanning multiple components and recursive wildcards. +/// +/// Because this function calls itself recursively with substrings, it's important that the +/// parameters be raw pointers instead of wcstring, which would be too expensive to construct for +/// all substrings. +/// +/// Args: +/// base_dir: the "working directory" against which the wildcard is to be resolved +/// wc: the wildcard string itself, e.g. foo*bar/baz (where * is acutally ANY_CHAR) +void wildcard_expander_t::expand(const wcstring &base_dir, const wchar_t *wc) { assert(wc != NULL); - - if (interrupted()) - { + + if (interrupted()) { return; } - - /* Get the current segment and compute interesting properties about it. */ + + // Get the current segment and compute interesting properties about it. const size_t wc_len = wcslen(wc); - const wchar_t * const next_slash = wcschr(wc, L'/'); + const wchar_t *const next_slash = wcschr(wc, L'/'); const bool is_last_segment = (next_slash == NULL); const size_t wc_segment_len = next_slash ? next_slash - wc : wc_len; const wcstring wc_segment = wcstring(wc, wc_segment_len); - const bool segment_has_wildcards = wildcard_has(wc_segment, true /* internal, i.e. look for ANY_CHAR instead of ? */); - - if (wc_segment.empty()) - { - /* Handle empty segment */ - assert(! segment_has_wildcards); - if (is_last_segment) - { + const bool segment_has_wildcards = + wildcard_has(wc_segment, true /* internal, i.e. look for ANY_CHAR instead of ? */); + + if (wc_segment.empty()) { + // Handle empty segment. + assert(!segment_has_wildcards); + if (is_last_segment) { this->expand_trailing_slash(base_dir); - } - else - { - /* Multiple adjacent slashes in the wildcard. Just skip them. */ + } else { + // Multiple adjacent slashes in the wildcard. Just skip them. this->expand(base_dir, next_slash + 1); } - } - else if (! segment_has_wildcards && ! is_last_segment) - { - /* Literal intermediate match. Note that we may not be able to actually read the directory (#2099) */ + } else if (!segment_has_wildcards && !is_last_segment) { + // Literal intermediate match. Note that we may not be able to actually read the directory + // (issue #2099). assert(next_slash != NULL); const wchar_t *wc_remainder = next_slash; - while (*wc_remainder == L'/') - { + while (*wc_remainder == L'/') { wc_remainder++; } - - /* This just trumps everything */ + + // This just trumps everything. size_t before = this->resolved_completions->size(); this->expand(base_dir + wc_segment + L'/', wc_remainder); - - /* Maybe try a fuzzy match (#94) if nothing was found with the literal match. Respect EXPAND_NO_DIRECTORY_ABBREVIATIONS (#2413). */ - bool allow_fuzzy = (this->flags & (EXPAND_FUZZY_MATCH | EXPAND_NO_FUZZY_DIRECTORIES)) == EXPAND_FUZZY_MATCH; - if (allow_fuzzy && this->resolved_completions->size() == before) - { + + // Maybe try a fuzzy match (#94) if nothing was found with the literal match. Respect + // EXPAND_NO_DIRECTORY_ABBREVIATIONS (issue #2413). + bool allow_fuzzy = (this->flags & (EXPAND_FUZZY_MATCH | EXPAND_NO_FUZZY_DIRECTORIES)) == + EXPAND_FUZZY_MATCH; + if (allow_fuzzy && this->resolved_completions->size() == before) { assert(this->flags & EXPAND_FOR_COMPLETIONS); DIR *base_dir_fd = open_dir(base_dir); - if (base_dir_fd != NULL) - { - this->expand_literal_intermediate_segment_with_fuzz(base_dir, base_dir_fd, wc_segment, wc_remainder); + if (base_dir_fd != NULL) { + this->expand_literal_intermediate_segment_with_fuzz(base_dir, base_dir_fd, + wc_segment, wc_remainder); closedir(base_dir_fd); } } - } - else - { - assert(! wc_segment.empty() && (segment_has_wildcards || is_last_segment)); + } else { + assert(!wc_segment.empty() && (segment_has_wildcards || is_last_segment)); DIR *dir = open_dir(base_dir); - if (dir) - { - if (is_last_segment) - { - /* Last wildcard segment, nonempty wildcard */ + if (dir) { + if (is_last_segment) { + // Last wildcard segment, nonempty wildcard. this->expand_last_segment(base_dir, dir, wc_segment); - } - else - { - /* Not the last segment, nonempty wildcard */ + } else { + // Not the last segment, nonempty wildcard. assert(next_slash != NULL); const wchar_t *wc_remainder = next_slash; - while (*wc_remainder == L'/') - { + while (*wc_remainder == L'/') { wc_remainder++; } this->expand_intermediate_segment(base_dir, dir, wc_segment, wc_remainder); } - - /* Recursive wildcards require special handling */ + + // Recursive wildcards require special handling. size_t asr_idx = wc_segment.find(ANY_STRING_RECURSIVE); - if (asr_idx != wcstring::npos) - { - /* Construct a "head + any" wildcard for matching stuff in this directory, and an "any + tail" wildcard for matching stuff in subdirectories. Note that the ANY_STRING_RECURSIVE character is present in both the head and the tail. */ + if (asr_idx != wcstring::npos) { + // Construct a "head + any" wildcard for matching stuff in this directory, and an + // "any + tail" wildcard for matching stuff in subdirectories. Note that the + // ANY_STRING_RECURSIVE character is present in both the head and the tail. const wcstring head_any(wc_segment, 0, asr_idx + 1); const wchar_t *any_tail = wc + asr_idx; assert(head_any.at(head_any.size() - 1) == ANY_STRING_RECURSIVE); @@ -1126,41 +889,45 @@ void wildcard_expander_t::expand(const wcstring &base_dir, const wchar_t *wc) } } - -int wildcard_expand_string(const wcstring &wc, const wcstring &working_directory, expand_flags_t flags, std::vector *output) -{ +int wildcard_expand_string(const wcstring &wc, const wcstring &working_directory, + expand_flags_t flags, std::vector *output) { assert(output != NULL); - /* Fuzzy matching only if we're doing completions */ + // Fuzzy matching only if we're doing completions. assert((flags & (EXPAND_FUZZY_MATCH | EXPAND_FOR_COMPLETIONS)) != EXPAND_FUZZY_MATCH); - - /* EXPAND_SPECIAL_FOR_CD requires DIRECTORIES_ONLY and EXPAND_FOR_COMPLETIONS and EXPAND_NO_DESCRIPTIONS */ + + // EXPAND_SPECIAL_FOR_CD requires DIRECTORIES_ONLY and EXPAND_FOR_COMPLETIONS and + // EXPAND_NO_DESCRIPTIONS. assert(!(flags & EXPAND_SPECIAL_FOR_CD) || - ((flags & DIRECTORIES_ONLY) && (flags & EXPAND_FOR_COMPLETIONS) && (flags & EXPAND_NO_DESCRIPTIONS))); - - /* Hackish fix for 1631. We are about to call c_str(), which will produce a string truncated at any embedded nulls. We could fix this by passing around the size, etc. However embedded nulls are never allowed in a filename, so we just check for them and return 0 (no matches) if there is an embedded null. */ - if (wc.find(L'\0') != wcstring::npos) - { + ((flags & DIRECTORIES_ONLY) && (flags & EXPAND_FOR_COMPLETIONS) && + (flags & EXPAND_NO_DESCRIPTIONS))); + + // Hackish fix for issue #1631. We are about to call c_str(), which will produce a string + // truncated at any embedded nulls. We could fix this by passing around the size, etc. However + // embedded nulls are never allowed in a filename, so we just check for them and return 0 (no + // matches) if there is an embedded null. + if (wc.find(L'\0') != wcstring::npos) { return 0; } - - /* Compute the prefix and base dir. The prefix is what we prepend for filesystem operations (i.e. the working directory), the base_dir is the part of the wildcard consumed thus far, which we also have to append. The difference is that the base_dir is returned as part of the expansion, and the prefix is not. - - Check for a leading slash. If we find one, we have an absolute path: the prefix is empty, the base dir is /, and the wildcard is the remainder. If we don't find one, the prefix is the working directory, there's base dir is empty. - */ + + // Compute the prefix and base dir. The prefix is what we prepend for filesystem operations + // (i.e. the working directory), the base_dir is the part of the wildcard consumed thus far, + // which we also have to append. The difference is that the base_dir is returned as part of the + // expansion, and the prefix is not. + // + // Check for a leading slash. If we find one, we have an absolute path: the prefix is empty, the + // base dir is /, and the wildcard is the remainder. If we don't find one, the prefix is the + // working directory, there's base dir is empty. wcstring prefix, base_dir, effective_wc; - if (string_prefixes_string(L"/", wc)) - { + if (string_prefixes_string(L"/", wc)) { prefix = L""; base_dir = L"/"; effective_wc = wc.substr(1); - } - else - { + } else { prefix = working_directory; base_dir = L""; effective_wc = wc; } - + wildcard_expander_t expander(prefix, base_dir, effective_wc.c_str(), flags, output); expander.expand(base_dir, wc.c_str()); return expander.status_code(); diff --git a/src/wildcard.h b/src/wildcard.h index 05e185f4c..1ed7cc893 100644 --- a/src/wildcard.h +++ b/src/wildcard.h @@ -1,84 +1,69 @@ -/** \file wildcard.h - - My own globbing implementation. Needed to implement this instead - of using libs globbing to support tab-expansion of globbed - paramaters. - -*/ +// My own globbing implementation. Needed to implement this instead of using libs globbing to +// support tab-expansion of globbed paramaters. #ifndef FISH_WILDCARD_H #define FISH_WILDCARD_H -#include #include +#include #include "common.h" -#include "expand.h" #include "complete.h" +#include "expand.h" -// Enumeration of all wildcard types -enum -{ - // Character representing any character except '/' (slash). +// Enumeration of all wildcard types. +enum { + /// Character representing any character except '/' (slash). ANY_CHAR = WILDCARD_RESERVED_BASE, - // Character representing any character string not containing '/' (slash). + /// Character representing any character string not containing '/' (slash). ANY_STRING, - // Character representing any character string. + /// Character representing any character string. ANY_STRING_RECURSIVE, - // This is a special psuedo-char that is not used other than to mark the - // end of the the special characters so we can sanity check the enum range. + /// This is a special psuedo-char that is not used other than to mark the + /// end of the the special characters so we can sanity check the enum range. ANY_SENTINAL }; -/** - Expand the wildcard by matching against the filesystem. +/// Expand the wildcard by matching against the filesystem. +/// +/// New strings are allocated using malloc and should be freed by the caller. +/// +/// wildcard_expand works by dividing the wildcard into segments at each directory boundary. Each +/// segment is processed separatly. All except the last segment are handled by matching the wildcard +/// segment against all subdirectories of matching directories, and recursively calling +/// wildcard_expand for matches. On the last segment, matching is made to any file, and all matches +/// are inserted to the list. +/// +/// If wildcard_expand encounters any errors (such as insufficient priviliges) during matching, no +/// error messages will be printed and wildcard_expand will continue the matching process. +/// +/// \param wc The wildcard string +/// \param working_directory The working directory +/// \param flags flags for the search. Can be any combination of EXPAND_FOR_COMPLETIONS and +/// EXECUTABLES_ONLY +/// \param out The list in which to put the output +/// +/// \return 1 if matches where found, 0 otherwise. Return -1 on abort (I.e. ^C was pressed). +int wildcard_expand_string(const wcstring &wc, const wcstring &working_directory, + expand_flags_t flags, std::vector *out); - New strings are allocated using malloc and should be freed by the caller. +/// Test whether the given wildcard matches the string. Does not perform any I/O. +/// +/// \param str The string to test +/// \param wc The wildcard to test against +/// \param leading_dots_fail_to_match if set, strings with leading dots are assumed to be hidden +/// files and are not matched +/// +/// \return true if the wildcard matched +bool wildcard_match(const wcstring &str, const wcstring &wc, + bool leading_dots_fail_to_match = false); - wildcard_expand works by dividing the wildcard into segments at - each directory boundary. Each segment is processed separatly. All - except the last segment are handled by matching the wildcard - segment against all subdirectories of matching directories, and - recursively calling wildcard_expand for matches. On the last - segment, matching is made to any file, and all matches are - inserted to the list. - - If wildcard_expand encounters any errors (such as insufficient - priviliges) during matching, no error messages will be printed and - wildcard_expand will continue the matching process. - - \param wc The wildcard string - \param working_directory The working directory - \param flags flags for the search. Can be any combination of EXPAND_FOR_COMPLETIONS and EXECUTABLES_ONLY - \param out The list in which to put the output - - \return 1 if matches where found, 0 otherwise. Return -1 on abort (I.e. ^C was pressed). - -*/ -int wildcard_expand_string(const wcstring &wc, const wcstring &working_directory, expand_flags_t flags, std::vector *out); - -/** - Test whether the given wildcard matches the string. Does not perform any I/O. - - \param str The string to test - \param wc The wildcard to test against - \param leading_dots_fail_to_match if set, strings with leading dots are assumed to be hidden files and are not matched - \return true if the wildcard matched -*/ -bool wildcard_match(const wcstring &str, const wcstring &wc, bool leading_dots_fail_to_match = false); - -/** Check if the specified string contains wildcards */ +/// Check if the specified string contains wildcards. bool wildcard_has(const wcstring &, bool internal); bool wildcard_has(const wchar_t *, bool 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 expand_flags, - complete_flags_t flags); +/// 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 expand_flags, complete_flags_t flags); #endif diff --git a/src/wutil.cpp b/src/wutil.cpp index 1cee8320a..c8b9e3f48 100644 --- a/src/wutil.cpp +++ b/src/wutil.cpp @@ -1,30 +1,27 @@ -/** \file wutil.c - Wide character equivalents of various standard unix - functions. -*/ +// Wide character equivalents of various standard unix functions. #include "config.h" -#include -#include -#include -#include -#include +#include +#include #include #include -#include -#include -#include -#include #include +#include #include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include #include +#include #include "common.h" #include "fallback.h" // IWYU pragma: keep -#include "wutil.h" // IWYU pragma: keep +#include "wutil.h" // IWYU pragma: keep typedef std::string cstring; @@ -34,65 +31,53 @@ const file_id_t kInvalidFileID = {(dev_t)-1LL, (ino_t)-1LL, (uint64_t)-1LL, -1, #ifdef MAXPATHLEN #define PATH_MAX MAXPATHLEN #else -/** - Fallback length of MAXPATHLEN. Just a hopefully sane value... -*/ +/// Fallback length of MAXPATHLEN. Hopefully a sane value. #define PATH_MAX 4096 #endif #endif -/* Lock to protect wgettext */ +/// Lock to protect wgettext. static pthread_mutex_t wgettext_lock; -/* Map used as cache by wgettext. */ +/// Map used as cache by wgettext. typedef std::map wgettext_map_t; static wgettext_map_t wgettext_map; -bool wreaddir_resolving(DIR *dir, const std::wstring &dir_path, std::wstring &out_name, bool *out_is_dir) -{ +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; out_name = str2wcstring(d->d_name); - if (out_is_dir) - { - /* The caller cares if this is a directory, so check */ + if (out_is_dir) { + // The caller cares if this is a directory, so check. bool is_dir = false; - - /* We may be able to skip stat, if the readdir can tell us the file type directly */ + + // We may be able to skip stat, if the readdir can tell us the file type directly. bool check_with_stat = true; #ifdef HAVE_STRUCT_DIRENT_D_TYPE - if (d->d_type == DT_DIR) - { - /* Known directory */ + if (d->d_type == DT_DIR) { + // Known directory. is_dir = true; check_with_stat = false; - } - 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. */ + } 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. check_with_stat = true; - } - else - { - /* Regular file */ + } else { + // Regular file. is_dir = false; check_with_stat = false; } -#endif // HAVE_STRUCT_DIRENT_D_TYPE - if (check_with_stat) - { - /* We couldn't determine the file type from the dirent; check by stat'ing it */ +#endif // HAVE_STRUCT_DIRENT_D_TYPE + if (check_with_stat) { + // We couldn't determine the file type from the dirent; check by stat'ing 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 - { + } else { is_dir = !!(S_ISDIR(buf.st_mode)); } } @@ -101,8 +86,7 @@ bool wreaddir_resolving(DIR *dir, const std::wstring &dir_path, std::wstring &ou return true; } -bool wreaddir(DIR *dir, std::wstring &out_name) -{ +bool wreaddir(DIR *dir, std::wstring &out_name) { struct dirent *d = readdir(dir); if (!d) return false; @@ -110,53 +94,45 @@ bool wreaddir(DIR *dir, std::wstring &out_name) return true; } -bool wreaddir_for_dirs(DIR *dir, wcstring *out_name) -{ +bool wreaddir_for_dirs(DIR *dir, wcstring *out_name) { struct dirent *result = NULL; - while (result == NULL) - { + while (result == NULL) { struct dirent *d = readdir(dir); if (!d) break; - + #if HAVE_STRUCT_DIRENT_D_TYPE - switch (d->d_type) - { - // These may be directories + switch (d->d_type) { case DT_DIR: case DT_LNK: - case DT_UNKNOWN: + case DT_UNKNOWN: { + // These may be directories. result = d; break; - - // Nothing else can - default: + } + default: { + // Nothing else can. break; + } } #else - /* We can't determine if it's a directory or not, so just return it */ + // We can't determine if it's a directory or not, so just return it. result = d; #endif } - if (result && out_name) - { + if (result && out_name) { *out_name = str2wcstring(result->d_name); } return result != NULL; } - -const wcstring wgetcwd() -{ +const wcstring wgetcwd() { wcstring retval; char *res = getcwd(NULL, 0); - if (res) - { + if (res) { retval = str2wcstring(res); free(res); - } - else - { + } else { debug(0, _(L"getcwd() failed with errno %d/%s"), errno, strerror(errno)); retval = wcstring(); } @@ -164,176 +140,147 @@ const wcstring wgetcwd() return retval; } -int wchdir(const wcstring &dir) -{ +int wchdir(const wcstring &dir) { cstring tmp = wcs2string(dir); return chdir(tmp.c_str()); } -FILE *wfopen(const wcstring &path, const char *mode) -{ +FILE *wfopen(const wcstring &path, const char *mode) { int permissions = 0, options = 0; size_t idx = 0; - switch (mode[idx++]) - { - case 'r': + switch (mode[idx++]) { + case 'r': { permissions = O_RDONLY; break; - case 'w': + } + case 'w': { permissions = O_WRONLY; options = O_CREAT | O_TRUNC; break; - case 'a': + } + case 'a': { permissions = O_WRONLY; options = O_CREAT | O_APPEND; break; - default: + } + default: { errno = EINVAL; return NULL; break; + } } - /* Skip binary */ - if (mode[idx] == 'b') - idx++; + // Skip binary. + if (mode[idx] == 'b') idx++; - /* Consider append option */ - if (mode[idx] == '+') - permissions = O_RDWR; + // Consider append option. + if (mode[idx] == '+') permissions = O_RDWR; int fd = wopen_cloexec(path, permissions | options, 0666); - if (fd < 0) - return NULL; + if (fd < 0) return NULL; FILE *result = fdopen(fd, mode); - if (result == NULL) - close(fd); + if (result == NULL) close(fd); return result; } -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; } } -static int wopen_internal(const wcstring &pathname, int flags, mode_t mode, bool cloexec) -{ +static int wopen_internal(const wcstring &pathname, int flags, mode_t mode, bool cloexec) { ASSERT_IS_NOT_FORKED_CHILD(); cstring tmp = wcs2string(pathname); - /* Prefer to use O_CLOEXEC. It has to both be defined and nonzero. */ +// Prefer to use O_CLOEXEC. It has to both be defined and nonzero. #ifdef O_CLOEXEC - if (cloexec && (O_CLOEXEC != 0)) - { + if (cloexec && (O_CLOEXEC != 0)) { 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; } return fd; - } -int wopen_cloexec(const wcstring &pathname, int flags, mode_t mode) -{ +int wopen_cloexec(const wcstring &pathname, int flags, mode_t mode) { return wopen_internal(pathname, flags, mode, true); } -DIR *wopendir(const wcstring &name) -{ +DIR *wopendir(const wcstring &name) { const cstring tmp = wcs2string(name); return opendir(tmp.c_str()); } -int wstat(const wcstring &file_name, struct stat *buf) -{ +int wstat(const wcstring &file_name, struct stat *buf) { const cstring tmp = wcs2string(file_name); return stat(tmp.c_str(), buf); } -int lwstat(const wcstring &file_name, struct stat *buf) -{ +int lwstat(const wcstring &file_name, struct stat *buf) { const cstring tmp = wcs2string(file_name); return lstat(tmp.c_str(), buf); } -int waccess(const wcstring &file_name, int mode) -{ +int waccess(const wcstring &file_name, int mode) { const cstring tmp = wcs2string(file_name); return access(tmp.c_str(), mode); } -int wunlink(const wcstring &file_name) -{ +int wunlink(const wcstring &file_name) { const cstring tmp = wcs2string(file_name); return unlink(tmp.c_str()); } -void wperror(const wchar_t *s) -{ +void wperror(const wchar_t *s) { int e = errno; - if (s[0] != L'\0') - { + if (s[0] != L'\0') { fwprintf(stderr, L"%ls: ", s); } fwprintf(stderr, L"%s\n", strerror(e)); } -int make_fd_nonblocking(int fd) -{ +int make_fd_nonblocking(int fd) { int flags = fcntl(fd, F_GETFL, 0); int err = 0; - if (!(flags & O_NONBLOCK)) - { + if (!(flags & O_NONBLOCK)) { err = fcntl(fd, F_SETFL, flags | O_NONBLOCK); } return err == -1 ? errno : 0; } -int make_fd_blocking(int fd) -{ +int make_fd_blocking(int fd) { int flags = fcntl(fd, F_GETFL, 0); int err = 0; - if (flags & O_NONBLOCK) - { + if (flags & O_NONBLOCK) { err = fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); } return err == -1 ? errno : 0; } -static inline void safe_append(char *buffer, const char *s, size_t buffsize) -{ +static inline void safe_append(char *buffer, const char *s, size_t buffsize) { strncat(buffer, s, buffsize - strlen(buffer) - 1); } -// In general, strerror is not async-safe, and therefore we cannot use it directly -// So instead we have to grub through sys_nerr and sys_errlist directly -// On GNU toolchain, this will produce a deprecation warning from the linker (!!), -// which appears impossible to suppress! -const char *safe_strerror(int err) -{ +// In general, strerror is not async-safe, and therefore we cannot use it directly. So instead we +// have to grub through sys_nerr and sys_errlist directly On GNU toolchain, this will produce a +// deprecation warning from the linker (!!), which appears impossible to suppress! +const char *safe_strerror(int err) { #if defined(__UCLIBC__) - // uClibc does not have sys_errlist, however, its strerror is believed to be async-safe - // See #808 + // uClibc does not have sys_errlist, however, its strerror is believed to be async-safe. + // See issue #808. return strerror(err); #elif defined(HAVE__SYS__ERRS) || defined(HAVE_SYS_ERRLIST) #ifdef HAVE_SYS_ERRLIST - if (err >= 0 && err < sys_nerr && sys_errlist[err] != NULL) - { + if (err >= 0 && err < sys_nerr && sys_errlist[err] != NULL) { return sys_errlist[err]; } #elif defined(HAVE__SYS__ERRS) @@ -342,15 +289,15 @@ const char *safe_strerror(int err) extern int _sys_num_err; if (err >= 0 && err < _sys_num_err) { - return &_sys_errs[_sys_index[err]]; + return &_sys_errs[_sys_index[err]]; } -#endif // either HAVE__SYS__ERRS or HAVE_SYS_ERRLIST +#endif // either HAVE__SYS__ERRS or HAVE_SYS_ERRLIST else -#endif // defined(HAVE__SYS__ERRS) || defined(HAVE_SYS_ERRLIST) +#endif // defined(HAVE__SYS__ERRS) || defined(HAVE_SYS_ERRLIST) { int saved_err = errno; - /* Use a shared buffer for this case */ + // Use a shared buffer for this case. static char buff[384]; char errnum_buff[64]; format_long_safe(errnum_buff, err); @@ -365,16 +312,14 @@ const char *safe_strerror(int err) } } -void safe_perror(const char *message) -{ - // Note we cannot use strerror, because on Linux it uses gettext, which is not safe +void safe_perror(const char *message) { + // Note we cannot use strerror, because on Linux it uses gettext, which is not safe. int err = errno; char buff[384]; buff[0] = '\0'; - if (message) - { + if (message) { safe_append(buff, message, sizeof buff); safe_append(buff, ": ", sizeof buff); } @@ -387,23 +332,18 @@ void safe_perror(const char *message) #ifdef HAVE_REALPATH_NULL -wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path) -{ +wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path) { cstring narrow_path = wcs2string(pathname); char *narrow_res = realpath(narrow_path.c_str(), NULL); - if (!narrow_res) - return NULL; + if (!narrow_res) return NULL; wchar_t *res; wcstring wide_res = str2wcstring(narrow_res); - if (resolved_path) - { + if (resolved_path) { wcslcpy(resolved_path, wide_res.c_str(), PATH_MAX); res = resolved_path; - } - else - { + } else { res = wcsdup(wide_res.c_str()); } @@ -414,24 +354,19 @@ wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path) #else -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; - if (!narrow_res) - return 0; + if (!narrow_res) return 0; const wcstring wide_res = str2wcstring(narrow_res); - if (resolved_path) - { + if (resolved_path) { wcslcpy(resolved_path, wide_res.c_str(), PATH_MAX); res = resolved_path; - } - else - { + } else { res = wcsdup(wide_res.c_str()); } return res; @@ -439,9 +374,7 @@ wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path) #endif - -wcstring wdirname(const wcstring &path) -{ +wcstring wdirname(const wcstring &path) { char *tmp = wcs2str(path.c_str()); char *narrow_res = dirname(tmp); wcstring result = format_string(L"%s", narrow_res); @@ -449,8 +382,7 @@ wcstring wdirname(const wcstring &path) return result; } -wcstring wbasename(const wcstring &path) -{ +wcstring wbasename(const wcstring &path) { char *tmp = wcs2str(path.c_str()); char *narrow_res = basename(tmp); wcstring result = format_string(L"%s", narrow_res); @@ -458,29 +390,24 @@ wcstring wbasename(const wcstring &path) return result; } -/* Really init wgettext */ -static void wgettext_really_init() -{ +// Really init wgettext. +static void wgettext_really_init() { pthread_mutex_init(&wgettext_lock, NULL); fish_bindtextdomain(PACKAGE_NAME, LOCALEDIR); fish_textdomain(PACKAGE_NAME); } -/** - For wgettext: Internal init function. Automatically called when a translation is first requested. -*/ -static void wgettext_init_if_necessary() -{ +/// For wgettext: Internal init function. Automatically called when a translation is first +/// requested. +static void wgettext_init_if_necessary() { static pthread_once_t once = PTHREAD_ONCE_INIT; pthread_once(&once, wgettext_really_init); } -const wchar_t *wgettext(const wchar_t *in) -{ - if (!in) - return in; +const wchar_t *wgettext(const wchar_t *in) { + if (!in) return in; - // preserve errno across this since this is often used in printing error messages + // Preserve errno across this since this is often used in printing error messages. int err = errno; wgettext_init_if_necessary(); @@ -489,59 +416,51 @@ const wchar_t *wgettext(const wchar_t *in) scoped_lock lock(wgettext_lock); wcstring &val = wgettext_map[key]; - if (val.empty()) - { + if (val.empty()) { cstring mbs_in = wcs2string(key); char *out = fish_gettext(mbs_in.c_str()); val = format_string(L"%s", out); } errno = err; - // The returned string is stored in the map - // TODO: If we want to shrink the map, this would be a problem + // The returned string is stored in the map. + // TODO: If we want to shrink the map, this would be a problem. return val.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); } -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); + 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) -{ +int fish_wcstoi(const wchar_t *str, wchar_t **endptr, int base) { long ret = wcstol(str, endptr, base); - if (ret > INT_MAX) - { + if (ret > INT_MAX) { ret = INT_MAX; errno = ERANGE; - } - else if (ret < INT_MIN) - { + } else if (ret < INT_MIN) { ret = INT_MIN; errno = ERANGE; } return (int)ret; } -file_id_t file_id_t::file_id_from_stat(const struct stat *buf) -{ +file_id_t file_id_t::file_id_from_stat(const struct stat *buf) { assert(buf != NULL); - + file_id_t result = {}; result.device = buf->st_dev; result.inode = buf->st_ino; result.size = buf->st_size; result.change_seconds = buf->st_ctime; result.mod_seconds = buf->st_mtime; - + #if STAT_HAVE_NSEC result.change_nanoseconds = buf->st_ctime_nsec; result.mod_nanoseconds = buf->st_mtime_nsec; @@ -555,74 +474,53 @@ file_id_t file_id_t::file_id_from_stat(const struct stat *buf) result.change_nanoseconds = 0; result.mod_nanoseconds = 0; #endif - + return result; } - -file_id_t file_id_for_fd(int fd) -{ +file_id_t file_id_for_fd(int fd) { file_id_t result = kInvalidFileID; struct stat buf = {}; - if (0 == fstat(fd, &buf)) - { + if (0 == fstat(fd, &buf)) { result = file_id_t::file_id_from_stat(&buf); } return result; } -file_id_t file_id_for_path(const wcstring &path) -{ +file_id_t file_id_for_path(const wcstring &path) { file_id_t result = kInvalidFileID; struct stat buf = {}; - if (0 == wstat(path, &buf)) - { + if (0 == wstat(path, &buf)) { result = file_id_t::file_id_from_stat(&buf); } return result; - } -bool file_id_t::operator==(const file_id_t &rhs) const -{ - return this->compare_file_id(rhs) == 0; -} +bool file_id_t::operator==(const file_id_t &rhs) const { return this->compare_file_id(rhs) == 0; } -bool file_id_t::operator!=(const file_id_t &rhs) const -{ - return ! (*this == rhs); -} +bool file_id_t::operator!=(const file_id_t &rhs) const { return !(*this == rhs); } -template -int compare(T a, T b) -{ - if (a < b) - { +template +int compare(T a, T b) { + if (a < b) { return -1; - } - else if (a > b) - { + } else if (a > b) { return 1; } return 0; } -int file_id_t::compare_file_id(const file_id_t &rhs) const -{ - /* Compare each field, stopping when we get to a non-equal field */ +int file_id_t::compare_file_id(const file_id_t &rhs) const { + // Compare each field, stopping when we get to a non-equal field. int ret = 0; - if (! ret) ret = compare(device, rhs.device); - if (! ret) ret = compare(inode, rhs.inode); - if (! ret) ret = compare(size, rhs.size); - if (! ret) ret = compare(change_seconds, rhs.change_seconds); - if (! ret) ret = compare(change_nanoseconds, rhs.change_nanoseconds); - if (! ret) ret = compare(mod_seconds, rhs.mod_seconds); - if (! ret) ret = compare(mod_nanoseconds, rhs.mod_nanoseconds); + if (!ret) ret = compare(device, rhs.device); + if (!ret) ret = compare(inode, rhs.inode); + if (!ret) ret = compare(size, rhs.size); + if (!ret) ret = compare(change_seconds, rhs.change_seconds); + if (!ret) ret = compare(change_nanoseconds, rhs.change_nanoseconds); + if (!ret) ret = compare(mod_seconds, rhs.mod_seconds); + if (!ret) ret = compare(mod_nanoseconds, rhs.mod_nanoseconds); return ret; } - -bool file_id_t::operator<(const file_id_t &rhs) const -{ - return this->compare_file_id(rhs) < 0; -} +bool file_id_t::operator<(const file_id_t &rhs) const { return this->compare_file_id(rhs) < 0; } diff --git a/src/wutil.h b/src/wutil.h index a4fee9004..9fc610964 100644 --- a/src/wutil.h +++ b/src/wutil.h @@ -1,141 +1,105 @@ -/** \file wutil.h - - Prototypes for wide character equivalents of various standard unix - functions. -*/ +// Prototypes for wide character equivalents of various standard unix functions. #ifndef FISH_WUTIL_H #define FISH_WUTIL_H -#include #include +#include +#include #include #include #include -#include #include "common.h" -/** - Wide character version of fopen(). This sets CLO_EXEC. -*/ +/// Wide character version of fopen(). This sets CLO_EXEC. FILE *wfopen(const wcstring &path, const char *mode); -/** Sets CLO_EXEC on a given fd */ +/// Sets CLO_EXEC on a given fd. bool set_cloexec(int fd); -/** Wide character version of open() that also sets the close-on-exec flag (atomically when possible). */ +/// Wide character version of open() that also sets the close-on-exec flag (atomically when +/// possible). int wopen_cloexec(const wcstring &pathname, int flags, mode_t mode = 0); -/** Mark an fd as nonblocking; returns errno or 0 on success */ +/// Mark an fd as nonblocking; returns errno or 0 on success. int make_fd_nonblocking(int fd); -/** Mark an fd as blocking; returns errno or 0 on success */ +/// Mark an fd as blocking; returns errno or 0 on success. int make_fd_blocking(int fd); -/** Wide character version of opendir(). Note that opendir() is guaranteed to set close-on-exec by POSIX (hooray). */ +/// Wide character version of opendir(). Note that opendir() is guaranteed to set close-on-exec by +/// POSIX (hooray). DIR *wopendir(const wcstring &name); -/** - Wide character version of stat(). -*/ +/// Wide character version of stat(). int wstat(const wcstring &file_name, struct stat *buf); -/** - Wide character version of lstat(). -*/ +/// Wide character version of lstat(). int lwstat(const wcstring &file_name, struct stat *buf); -/** - Wide character version of access(). -*/ +/// Wide character version of access(). int waccess(const wcstring &pathname, int mode); -/** - Wide character version of unlink(). -*/ +/// Wide character version of unlink(). int wunlink(const wcstring &pathname); -/** - Wide character version of perror(). -*/ +/// Wide character version of perror(). void wperror(const wchar_t *s); -/** - Async-safe version of perror(). -*/ +/// Async-safe version of perror(). void safe_perror(const char *message); -/** - Async-safe version of strerror(). -*/ +/// Async-safe version of strerror(). const char *safe_strerror(int err); -// Wide character version of getcwd(). +/// Wide character version of getcwd(). const wcstring wgetcwd(); -/** - Wide character version of chdir() -*/ +/// Wide character version of chdir(). int wchdir(const wcstring &dir); -/** - Wide character version of realpath function. Just like the GNU - version of realpath, wrealpath will accept 0 as the value for the - second argument, in which case the result will be allocated using - malloc, and must be free'd by the user. -*/ +/// Wide character version of realpath function. Just like the GNU version of realpath, wrealpath +/// will accept 0 as the value for the second argument, in which case the result will be allocated +/// using malloc, and must be free'd by the user. wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path); -/** - Wide character version of readdir() -*/ +/// Wide character version of readdir(). bool wreaddir(DIR *dir, std::wstring &out_name); -bool wreaddir_resolving(DIR *dir, const std::wstring &dir_path, std::wstring &out_name, bool *out_is_dir); +bool wreaddir_resolving(DIR *dir, const std::wstring &dir_path, std::wstring &out_name, + bool *out_is_dir); -/** - Like wreaddir, but skip items that are known to not be directories. - If this requires a stat (i.e. the file is a symlink), then return it. - Note that this does not guarantee that everything returned is a directory, - it's just an optimization for cases where we would check for directories anyways. -*/ +/// Like wreaddir, but skip items that are known to not be directories. If this requires a stat +/// (i.e. the file is a symlink), then return it. Note that this does not guarantee that everything +/// returned is a directory, it's just an optimization for cases where we would check for +/// directories anyways. bool wreaddir_for_dirs(DIR *dir, wcstring *out_name); -/** - Wide character version of dirname() -*/ +/// Wide character version of dirname(). std::wstring wdirname(const std::wstring &path); -/** - Wide character version of basename() -*/ +/// Wide character version of basename(). std::wstring wbasename(const std::wstring &path); -/** - Wide character wrapper around the gettext function. For historic - reasons, unlike the real gettext function, wgettext takes care of - setting the correct domain, etc. using the textdomain and - bindtextdomain functions. This should probably be moved out of - wgettext, so that wgettext will be nothing more than a wrapper - around gettext, like all other functions in this file. -*/ +/// Wide character wrapper around the gettext function. For historic reasons, unlike the real +/// gettext function, wgettext takes care of setting the correct domain, etc. using the textdomain +/// and bindtextdomain functions. This should probably be moved out of 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); -/** - Wide character version of mkdir -*/ +/// Wide character version of mkdir. int wmkdir(const wcstring &dir, int mode); -/** - Wide character version of rename -*/ +/// Wide character version of rename. 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); +/// 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); -/** Class for representing a file's inode. We use this to detect and avoid symlink loops, among other things. While an inode / dev pair is sufficient to distinguish co-existing files, Linux seems to aggressively re-use inodes, so it cannot determine if a file has been deleted (ABA problem). Therefore we include richer information. */ -struct file_id_t -{ +/// Class for representing a file's inode. We use this to detect and avoid symlink loops, among +/// other things. While an inode / dev pair is sufficient to distinguish co-existing files, Linux +/// seems to aggressively re-use inodes, so it cannot determine if a file has been deleted (ABA +/// problem). Therefore we include richer information. +struct file_id_t { dev_t device; ino_t inode; uint64_t size; @@ -143,16 +107,16 @@ struct file_id_t long change_nanoseconds; time_t mod_seconds; long mod_nanoseconds; - + bool operator==(const file_id_t &rhs) const; bool operator!=(const file_id_t &rhs) const; - - // Used to permit these as keys in std::map + + // Used to permit these as keys in std::map. bool operator<(const file_id_t &rhs) const; - + static file_id_t file_id_from_stat(const struct stat *buf); - - private: + + private: int compare_file_id(const file_id_t &rhs) const; }; @@ -161,5 +125,4 @@ file_id_t file_id_for_path(const wcstring &path); extern const file_id_t kInvalidFileID; - #endif From fc44cffac50e6096528f2bde456d91a4e40ea7c9 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Tue, 3 May 2016 16:23:30 -0700 Subject: [PATCH 214/363] restyle switch blocks to match project style I missed restyling a few "switch" blocks to make them consistent with the rest of the code base. This fixes that oversight. This should be the final step in restyling the C++ code to have a consistent style. This also includes a few trivial cleanups elsewhere. I also missed restyling the "complete" module when working my way from a to z so this final change includes restyling that module. Total lint errors decreased 36%. Cppcheck errors went from 47 to 24. Oclint P2 errors went from 819 to 778. Oclint P3 errors went from 3252 to 1842. Resolves #2902. --- src/autoload.cpp | 10 +- src/autoload.h | 2 +- src/builtin.cpp | 8 +- src/builtin_printf.cpp | 15 +- src/builtin_set.cpp | 7 +- src/color.cpp | 18 +- src/complete.cpp | 1904 ++++++++++++++++------------------------ src/complete.h | 326 +++---- src/exec.cpp | 17 +- src/history.cpp | 17 +- src/history.h | 4 +- src/input.cpp | 6 +- src/key_reader.cpp | 58 +- src/pager.cpp | 18 +- src/parse_util.cpp | 40 +- src/tokenizer.cpp | 3 +- src/wgetopt.cpp | 18 +- src/wgetopt.h | 2 +- src/wildcard.cpp | 15 +- 19 files changed, 1016 insertions(+), 1472 deletions(-) diff --git a/src/autoload.cpp b/src/autoload.cpp index 6f7c39058..31f6e6df3 100644 --- a/src/autoload.cpp +++ b/src/autoload.cpp @@ -1,18 +1,18 @@ // The classes responsible for autoloading functions and completions. #include #include +#include +#include +#include #include +#include #include #include #include +#include #include #include #include -#include -#include -#include -#include -#include #include "autoload.h" #include "common.h" diff --git a/src/autoload.h b/src/autoload.h index 5b1d5dfe4..1d6d4a618 100644 --- a/src/autoload.h +++ b/src/autoload.h @@ -3,10 +3,10 @@ #define FISH_AUTOLOAD_H #include +#include #include #include #include -#include #include "common.h" #include "lru.h" diff --git a/src/builtin.cpp b/src/builtin.cpp index 70b379b1f..8ed2858ba 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -1,4 +1,3 @@ -// // Functions for executing builtin functions. // // How to add a new builtin function: @@ -1895,17 +1894,18 @@ static int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) nchars = fish_wcstoi(w.woptarg, &end, 10); if (errno || *end != 0) { switch (errno) { - case ERANGE: + case ERANGE: { streams.err.append_format(_(L"%ls: Argument '%ls' is out of range\n"), argv[0], w.woptarg); builtin_print_help(parser, streams, argv[0], streams.err); return STATUS_BUILTIN_ERROR; - - default: + } + default: { streams.err.append_format( _(L"%ls: Argument '%ls' must be an integer\n"), argv[0], w.woptarg); builtin_print_help(parser, streams, argv[0], streams.err); return STATUS_BUILTIN_ERROR; + } } } break; diff --git a/src/builtin_printf.cpp b/src/builtin_printf.cpp index a3df00d39..a755d27ab 100644 --- a/src/builtin_printf.cpp +++ b/src/builtin_printf.cpp @@ -596,21 +596,24 @@ int builtin_printf_state_t::print_formatted(const wchar_t *format, int argc, wch for (;; f++, direc_length++) { switch (*f) { case L'I': - case L'\'': + case L'\'': { modify_allowed_format_specifiers(ok, "aAceEosxX", false); break; + } case '-': case '+': - case ' ': + case ' ': { break; - case L'#': + } + case L'#': { modify_allowed_format_specifiers(ok, "cdisu", false); break; - case '0': + } + case '0': { modify_allowed_format_specifiers(ok, "cs", false); break; - default: - goto no_more_flag_characters; + } + default: { goto no_more_flag_characters; } } } no_more_flag_characters:; diff --git a/src/builtin_set.cpp b/src/builtin_set.cpp index 2bc60d8b8..f4f49baf9 100644 --- a/src/builtin_set.cpp +++ b/src/builtin_set.cpp @@ -412,12 +412,11 @@ int builtin_set(parser_t &parser, io_streams_t &streams, wchar_t **argv) { builtin_print_help(parser, streams, argv[0], streams.out); return 0; } - case '?': + case '?': { builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]); return 1; - - default: - break; + } + default: { break; } } } diff --git a/src/color.cpp b/src/color.cpp index 006518052..6b920d15d 100644 --- a/src/color.cpp +++ b/src/color.cpp @@ -311,20 +311,26 @@ rgb_color_t::rgb_color_t(const std::string &str) : type(), flags() { wcstring rgb_color_t::description() const { switch (type) { - case type_none: + case type_none: { return L"none"; - case type_named: + } + case type_named: { return format_string(L"named(%d: %ls)", (int)data.name_idx, name_for_color_idx(data.name_idx)); - case type_rgb: + } + case type_rgb: { return format_string(L"rgb(0x%02x%02x%02x)", data.color.rgb[0], data.color.rgb[1], data.color.rgb[2]); - case type_reset: + } + case type_reset: { return L"reset"; - case type_normal: + } + case type_normal: { return L"normal"; - default: + } + default: { abort(); return L""; + } } } diff --git a/src/complete.cpp b/src/complete.cpp index db63e3487..198ad2e31 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -1,143 +1,114 @@ /** \file complete.c Functions related to tab-completion. - These functions are used for storing and retrieving tab-completion data, as well as for performing tab-completion. + These functions are used for storing and retrieving tab-completion data, as well as for performing + tab-completion. */ #include +#include +#include +#include #include #include #include -#include -#include #include #include #include +#include #include #include #include -#include -#include -#include "fallback.h" // IWYU pragma: keep -#include "util.h" -#include "wildcard.h" -#include "proc.h" -#include "parser.h" -#include "function.h" -#include "complete.h" +#include "autoload.h" #include "builtin.h" +#include "common.h" +#include "complete.h" #include "env.h" #include "exec.h" #include "expand.h" -#include "common.h" -#include "parse_util.h" -#include "wutil.h" // IWYU pragma: keep -#include "path.h" -#include "parse_tree.h" +#include "fallback.h" // IWYU pragma: keep +#include "function.h" #include "iothread.h" -#include "autoload.h" #include "parse_constants.h" +#include "parse_tree.h" +#include "parse_util.h" +#include "parser.h" +#include "path.h" +#include "proc.h" +#include "util.h" +#include "wildcard.h" +#include "wutil.h" // IWYU pragma: keep -/* - Completion description strings, mostly for different types of files, such as sockets, block devices, etc. +// Completion description strings, mostly for different types of files, such as sockets, block +// devices, etc. +// +// There are a few more completion description strings defined in expand.c. Maybe all completion +// description strings should be defined in the same file? - There are a few more completion description strings defined in - expand.c. Maybe all completion description strings should be defined - in the same file? -*/ +/// Description for ~USER completion. +#define COMPLETE_USER_DESC _(L"Home for %ls") -/** - Description for ~USER completion -*/ -#define COMPLETE_USER_DESC _( L"Home for %ls" ) +/// Description for short variables. The value is concatenated to this description. +#define COMPLETE_VAR_DESC_VAL _(L"Variable: %ls") -/** - Description for short variables. The value is concatenated to this description -*/ -#define COMPLETE_VAR_DESC_VAL _( L"Variable: %ls" ) - -/** - The special cased translation macro for completions. The empty - string needs to be special cased, since it can occur, and should - not be translated. (Gettext returns the version information as the - response) -*/ +/// The special cased translation macro for completions. The empty string needs to be special cased, +/// since it can occur, and should not be translated. (Gettext returns the version information as +/// the response). #ifdef USE_GETTEXT -static const wchar_t *C_(const wcstring &s) -{ - return s.empty() ? L"" : wgettext(s.c_str()); -} +static const wchar_t *C_(const wcstring &s) { return s.empty() ? L"" : wgettext(s.c_str()); } #else -static const wcstring &C_(const wcstring &s) -{ - return s; -} +static const wcstring &C_(const wcstring &s) { return s; } #endif static void complete_load(const wcstring &name, bool reload); -/* Testing apparatus */ +// Testing apparatus. const wcstring_list_t *s_override_variable_names = NULL; -void complete_set_variable_names(const wcstring_list_t *names) -{ +void complete_set_variable_names(const wcstring_list_t *names) { s_override_variable_names = names; } -static inline wcstring_list_t complete_get_variable_names(void) -{ - if (s_override_variable_names != NULL) - { +static inline wcstring_list_t complete_get_variable_names(void) { + if (s_override_variable_names != NULL) { return *s_override_variable_names; - } - else - { + } else { return env_get_names(0); } } -/** - Struct describing a completion option entry. - - If option is empty, the comp field must not be - empty and contains a list of arguments to the command. - - The type field determines how the option is to be interpreted: - either empty (args_only) or short, single-long ("old") or double-long ("GNU"). - An invariant is that the option is empty if and only if the type is args_only. - - If option is non-empty, it specifies a switch - for the command. If \c comp is also not empty, it contains a list - of non-switch arguments that may only follow directly after the - specified switch. -*/ -typedef struct complete_entry_opt -{ - /* Text of the option (like 'foo') */ +/// Struct describing a completion option entry. +/// +/// If option is empty, the comp field must not be empty and contains a list of arguments to the +/// command. +/// +/// The type field determines how the option is to be interpreted: either empty (args_only) or +/// short, single-long ("old") or double-long ("GNU"). An invariant is that the option is empty if +/// and only if the type is args_only. +/// +/// If option is non-empty, it specifies a switch for the command. If \c comp is also not empty, it +/// contains a list of non-switch arguments that may only follow directly after the specified +/// switch. +typedef struct complete_entry_opt { + // Text of the option (like 'foo'). wcstring option; - /* Type of the option: args-oly, short, single_long, or double_long */ + // Type of the option: args-oly, short, single_long, or double_long. complete_option_type_t type; - /** Arguments to the option */ + // Arguments to the option. wcstring comp; - /** Description of the completion */ + // Description of the completion. wcstring desc; - /** Condition under which to use the option */ + // 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. */ + // 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; - /** Completion flags */ + // Completion flags. complete_flags_t flags; - const wcstring localized_desc() const - { - return C_(desc); - } - - size_t expected_dash_count() const - { - switch (this->type) - { + const wcstring localized_desc() const { return C_(desc); } + + size_t expected_dash_count() const { + switch (this->type) { case option_type_args_only: return 0; case option_type_short: @@ -148,65 +119,49 @@ typedef struct complete_entry_opt } assert(0 && "Unreachable"); } - + } complete_entry_opt_t; -/* Last value used in the order field of completion_entry_t */ +// Last value used in the order field of completion_entry_t. static unsigned int kCompleteOrder = 0; -/** - Struct describing a command completion -*/ +/// Struct describing a command completion. typedef std::list option_list_t; -class completion_entry_t -{ -public: - /** List of all options */ +class completion_entry_t { + public: + /// List of all options. option_list_t options; -public: - - /** Command string */ + public: + /// Command string. const wcstring cmd; - - /** True if command is a path */ + /// True if command is a path. const bool cmd_is_path; - - /** True if no other options than the ones supplied are possible */ + /// 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. */ + /// Order for when this completion was created. This aids in outputting completions sorted by + /// time. const unsigned int order; - /** Getters for option list. */ + /// Getters for option list. const option_list_t &get_options() const; - /** Adds or removes an option. */ + /// Adds or removes an option. void add_option(const complete_entry_opt_t &opt); bool remove_option(const wcstring &option, complete_option_type_t type); - completion_entry_t(const wcstring &c, bool type, bool author) : - cmd(c), - cmd_is_path(type), - authoritative(author), - order(++kCompleteOrder) - { - } + completion_entry_t(const wcstring &c, bool type, bool author) + : cmd(c), cmd_is_path(type), authoritative(author), order(++kCompleteOrder) {} }; -/** Set of all completion entries */ -struct completion_entry_set_comparer -{ +/// Set of all completion entries. +struct completion_entry_set_comparer { /** Comparison for std::set */ - bool operator()(const completion_entry_t &p1, const completion_entry_t &p2) const - { - /* Paths always come last for no particular reason */ - if (p1.cmd_is_path != p2.cmd_is_path) - { + bool operator()(const completion_entry_t &p1, const completion_entry_t &p2) const { + // Paths always come last for no particular reason. + 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; } } @@ -214,62 +169,51 @@ struct completion_entry_set_comparer typedef std::set completion_entry_set_t; 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) -{ +// 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) { return p1->order < p2->order; } -/** The lock that guards the list of completion entries */ +/// The lock that guards the list of completion entries. static pthread_mutex_t completion_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_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_lock); return options; } -completion_t::~completion_t() -{ -} +completion_t::~completion_t() {} -/* Clear the COMPLETE_AUTO_SPACE flag, and set COMPLETE_NO_SPACE appropriately depending on the suffix of the string */ -static complete_flags_t resolve_auto_space(const wcstring &comp, complete_flags_t flags) -{ - if (flags & COMPLETE_AUTO_SPACE) - { +// Clear the COMPLETE_AUTO_SPACE flag, and set COMPLETE_NO_SPACE appropriately depending on the +// suffix of the string. +static complete_flags_t resolve_auto_space(const wcstring &comp, complete_flags_t flags) { + if (flags & COMPLETE_AUTO_SPACE) { flags = flags & ~COMPLETE_AUTO_SPACE; size_t len = comp.size(); - if (len > 0 && (wcschr(L"/=@:", comp.at(len-1)) != 0)) - flags |= COMPLETE_NO_SPACE; + if (len > 0 && (wcschr(L"/=@:", comp.at(len - 1)) != 0)) flags |= COMPLETE_NO_SPACE; } return flags; } -/* completion_t functions. Note that the constructor resolves flags! */ -completion_t::completion_t(const wcstring &comp, const wcstring &desc, string_fuzzy_match_t mat, complete_flags_t flags_val) : - completion(comp), - description(desc), - match(mat), - flags(resolve_auto_space(comp, flags_val)) -{ -} +// completion_t functions. Note that the constructor resolves flags! +completion_t::completion_t(const wcstring &comp, const wcstring &desc, string_fuzzy_match_t mat, + complete_flags_t flags_val) + : completion(comp), description(desc), match(mat), flags(resolve_auto_space(comp, flags_val)) {} -completion_t::completion_t(const completion_t &him) : completion(him.completion), description(him.description), match(him.match), flags(him.flags) -{ -} +completion_t::completion_t(const completion_t &him) + : completion(him.completion), + description(him.description), + match(him.match), + flags(him.flags) {} -completion_t &completion_t::operator=(const completion_t &him) -{ - if (this != &him) - { +completion_t &completion_t::operator=(const completion_t &him) { + if (this != &him) { this->completion = him.completion; this->description = him.description; this->match = him.match; @@ -278,140 +222,105 @@ completion_t &completion_t::operator=(const completion_t &him) return *this; } -bool completion_t::is_naturally_less_than(const completion_t &a, const completion_t &b) -{ +bool completion_t::is_naturally_less_than(const completion_t &a, const completion_t &b) { return wcsfilecmp(a.completion.c_str(), b.completion.c_str()) < 0; } -bool completion_t::is_alphabetically_equal_to(const completion_t &a, const completion_t &b) -{ +bool completion_t::is_alphabetically_equal_to(const completion_t &a, const completion_t &b) { return a.completion == b.completion; } -void completion_t::prepend_token_prefix(const wcstring &prefix) -{ - if (this->flags & COMPLETE_REPLACES_TOKEN) - { +void completion_t::prepend_token_prefix(const wcstring &prefix) { + if (this->flags & COMPLETE_REPLACES_TOKEN) { this->completion.insert(0, prefix); } } - -static bool compare_completions_by_match_type(const completion_t &a, const completion_t &b) -{ +static bool compare_completions_by_match_type(const completion_t &a, const completion_t &b) { return a.match.type < b.match.type; } -void completions_sort_and_prioritize(std::vector *comps) -{ - /* Find the best match type */ +void completions_sort_and_prioritize(std::vector *comps) { + // Find the best match type. fuzzy_match_type_t best_type = fuzzy_match_none; - for (size_t i=0; i < comps->size(); i++) - { + for (size_t i = 0; i < comps->size(); i++) { best_type = std::min(best_type, comps->at(i).match.type); } - /* If the best type is an exact match, reduce it to prefix match. Otherwise a tab completion will only show one match if it matches a file exactly. (see issue #959) */ - if (best_type == fuzzy_match_exact) - { + // If the best type is an exact match, reduce it to prefix match. Otherwise a tab completion + // will only show one match if it matches a file exactly. (see issue #959). + if (best_type == fuzzy_match_exact) { best_type = fuzzy_match_prefix; } - - /* Throw out completions whose match types are less suitable than the best. */ + + // Throw out completions whose match types are less suitable than the best. size_t i = comps->size(); - while (i--) - { - if (comps->at(i).match.type > best_type) - { + while (i--) { + if (comps->at(i).match.type > best_type) { comps->erase(comps->begin() + i); } } - - /* Remove duplicates */ + + // Remove duplicates. sort(comps->begin(), comps->end(), completion_t::is_naturally_less_than); - comps->erase(std::unique(comps->begin(), comps->end(), completion_t::is_alphabetically_equal_to), comps->end()); - - /* Sort the remainder by match type. They're already sorted alphabetically */ + comps->erase( + std::unique(comps->begin(), comps->end(), completion_t::is_alphabetically_equal_to), + comps->end()); + + // Sort the remainder by match type. They're already sorted alphabetically. stable_sort(comps->begin(), comps->end(), compare_completions_by_match_type); } -/** Class representing an attempt to compute completions */ -class completer_t -{ +/// Class representing an attempt to compute completions. +class completer_t { const completion_request_flags_t flags; const wcstring initial_cmd; std::vector completions; - const env_vars_snapshot_t &vars; //transient, stack-allocated + const env_vars_snapshot_t &vars; // transient, stack-allocated - /** Table of completions conditions that have already been tested and the corresponding test results */ + /// Table of completions conditions that have already been tested and the corresponding test + /// results. typedef std::map condition_cache_t; condition_cache_t condition_cache; - enum complete_type_t - { - COMPLETE_DEFAULT, - COMPLETE_AUTOSUGGEST - }; + enum complete_type_t { COMPLETE_DEFAULT, COMPLETE_AUTOSUGGEST }; - complete_type_t type() const - { - return (flags & COMPLETION_REQUEST_AUTOSUGGESTION) ? COMPLETE_AUTOSUGGEST : COMPLETE_DEFAULT; + complete_type_t type() const { + return (flags & COMPLETION_REQUEST_AUTOSUGGESTION) ? COMPLETE_AUTOSUGGEST + : COMPLETE_DEFAULT; } - bool wants_descriptions() const - { - return !!(flags & COMPLETION_REQUEST_DESCRIPTIONS); + bool wants_descriptions() const { return !!(flags & COMPLETION_REQUEST_DESCRIPTIONS); } + + bool fuzzy() const { return !!(flags & COMPLETION_REQUEST_FUZZY_MATCH); } + + fuzzy_match_type_t max_fuzzy_match_type() const { + // If we are doing fuzzy matching, request all types; if not request only prefix matching. + return (flags & COMPLETION_REQUEST_FUZZY_MATCH) ? fuzzy_match_none + : fuzzy_match_prefix_case_insensitive; } - bool fuzzy() const - { - return !!(flags & COMPLETION_REQUEST_FUZZY_MATCH); - } + public: + completer_t(const wcstring &c, completion_request_flags_t f, const env_vars_snapshot_t &evs) + : flags(f), initial_cmd(c), vars(evs) {} - fuzzy_match_type_t max_fuzzy_match_type() const - { - /* If we are doing fuzzy matching, request all types; if not request only prefix matching */ - return (flags & COMPLETION_REQUEST_FUZZY_MATCH) ? fuzzy_match_none : fuzzy_match_prefix_case_insensitive; - } - - -public: - completer_t(const wcstring &c, completion_request_flags_t f, const env_vars_snapshot_t &evs) : - flags(f), - initial_cmd(c), - vars(evs) - { - } - - 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 complete_param(const wcstring &cmd_orig, - const wcstring &popt, - const wcstring &str, + 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, bool handle_as_special_cd = false); - + void complete_param_expand(const wcstring &str, bool do_file, + bool handle_as_special_cd = false); + void complete_special_cd(const wcstring &str); - 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, bool use_implicit_cd); - void complete_from_args(const wcstring &str, - const wcstring &args, - const wcstring &desc, + void complete_from_args(const wcstring &str, const wcstring &args, const wcstring &desc, complete_flags_t flags); void complete_cmd_desc(const wcstring &str); @@ -420,56 +329,51 @@ public: 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 - { - /* Never do command substitution in autosuggestions. Sadly, we also can't yet do job expansion because it's not thread safe. */ + 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 (this->type() == COMPLETE_AUTOSUGGEST) - result |= EXPAND_SKIP_CMDSUBST; + if (this->type() == COMPLETE_AUTOSUGGEST) result |= EXPAND_SKIP_CMDSUBST; - /* Allow fuzzy matching */ - if (this->fuzzy()) - result |= EXPAND_FUZZY_MATCH; + // Allow fuzzy matching. + if (this->fuzzy()) result |= EXPAND_FUZZY_MATCH; return result; } }; -/* Autoloader for completions */ -class completion_autoload_t : public autoload_t -{ -public: +/// Autoloader for completions. +class completion_autoload_t : public autoload_t { + public: completion_autoload_t(); virtual void command_removed(const wcstring &cmd); }; static completion_autoload_t completion_autoloader; -/** Constructor */ -completion_autoload_t::completion_autoload_t() : autoload_t(L"fish_complete_path", NULL, 0) -{ -} +// Constructor +completion_autoload_t::completion_autoload_t() : autoload_t(L"fish_complete_path", NULL, 0) {} -/** Callback when an autoloaded completion is removed */ -void completion_autoload_t::command_removed(const wcstring &cmd) -{ +/// Callback when an autoloaded completion is removed. +void completion_autoload_t::command_removed(const wcstring &cmd) { complete_remove_all(cmd, false /* not a path */); } - -/** Create a new completion entry. */ -void append_completion(std::vector *completions, const wcstring &comp, const wcstring &desc, complete_flags_t flags, string_fuzzy_match_t match) -{ - /* If we just constructed the completion and used push_back, we would get two string copies. Try to avoid that by making a stubby completion in the vector first, and then copying our string in. Note that completion_t's constructor will munge 'flags' so it's important that we pass those to the constructor. - - Nasty hack for #1241 - since the constructor needs the completion string to resolve AUTO_SPACE, and we aren't providing it with the completion, we have to do the resolution ourselves. We should get this resolving out of the constructor. - */ +/// Create a new completion entry. +void append_completion(std::vector *completions, const wcstring &comp, + const wcstring &desc, complete_flags_t flags, string_fuzzy_match_t match) { + // If we just constructed the completion and used push_back, we would get two string copies. Try + // to avoid that by making a stubby completion in the vector first, and then copying our string + // in. Note that completion_t's constructor will munge 'flags' so it's important that we pass + // those to the constructor. + // + // Nasty hack for #1241 - since the constructor needs the completion string to resolve + // AUTO_SPACE, and we aren't providing it with the completion, we have to do the resolution + // ourselves. We should get this resolving out of the constructor. assert(completions != NULL); const wcstring empty; completions->push_back(completion_t(empty, empty, match, resolve_auto_space(comp, flags))); @@ -478,23 +382,17 @@ void append_completion(std::vector *completions, const wcstring &c last->description = desc; } -/** - Test if the specified script returns zero. The result is cached, so - that if multiple completions use the same condition, it needs only - 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) -{ - if (condition.empty()) - { -// fwprintf( stderr, L"No condition specified\n" ); +/// Test if the specified script returns zero. The result is cached, so that if multiple completions +/// use the same condition, it needs only 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) { + if (condition.empty()) { + // fwprintf( stderr, L"No condition specified\n" ); return 1; } - if (this->type() == COMPLETE_AUTOSUGGEST) - { - /* Autosuggestion can't support conditions */ + if (this->type() == COMPLETE_AUTOSUGGEST) { + // Autosuggestion can't support conditions. return 0; } @@ -502,66 +400,51 @@ 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()) - { - /* Compute new value and reinsert it */ + if (cached_entry == condition_cache.end()) { + // Compute new value and reinsert it. test_res = (0 == exec_subshell(condition, false /* don't apply exit status */)); condition_cache[condition] = test_res; - } - else - { - /* Use the old value */ + } else { + // Use the old value. test_res = cached_entry->second; } return test_res; } - -/** 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) -{ +/// Locate the specified entry. Create it if it doesn't exist. Must be called while locked. +static completion_entry_t &complete_get_exact_entry(const wcstring &cmd, bool cmd_is_path) { ASSERT_IS_LOCKED(completion_lock); std::pair ins = completion_set.insert(completion_entry_t(cmd, cmd_is_path, false)); - // NOTE SET_ELEMENTS_ARE_IMMUTABLE: Exposing mutable access here is only - // okay as long as callers do not change any field that matters to ordering - // - affecting order without telling std::set invalidates its internal state - return const_cast(*ins.first); + // NOTE SET_ELEMENTS_ARE_IMMUTABLE: Exposing mutable access here is only okay as long as callers + // do not change any field that matters to ordering - affecting order without telling std::set + // invalidates its internal state. + return const_cast(*ins.first); } - -void complete_set_authoritative(const wchar_t *cmd, bool cmd_is_path, bool authoritative) -{ - CHECK(cmd,); +void complete_set_authoritative(const wchar_t *cmd, bool cmd_is_path, bool authoritative) { + CHECK(cmd, ); scoped_lock lock(completion_lock); completion_entry_t &c = complete_get_exact_entry(cmd, cmd_is_path); c.authoritative = authoritative; } - -void complete_add(const wchar_t *cmd, - bool cmd_is_path, - const wcstring &option, - complete_option_type_t option_type, - int result_mode, - const wchar_t *condition, - const wchar_t *comp, - const wchar_t *desc, - complete_flags_t flags) -{ - CHECK(cmd,); - // option should be empty iff the option type is arguments only +void complete_add(const wchar_t *cmd, bool cmd_is_path, const wcstring &option, + complete_option_type_t option_type, int result_mode, const wchar_t *condition, + const wchar_t *comp, const wchar_t *desc, complete_flags_t flags) { + CHECK(cmd, ); + // option should be empty iff the option type is arguments only. assert(option.empty() == (option_type == option_type_args_only)); - /* Lock the lock that allows us to edit the completion entry list */ + // Lock the lock that allows us to edit the completion entry list. scoped_lock lock(completion_lock); completion_entry_t &c = complete_get_exact_entry(cmd, cmd_is_path); - /* Create our new option */ + // Create our new option. complete_entry_opt_t opt; opt.option = option; opt.type = option_type; @@ -575,302 +458,227 @@ void complete_add(const wchar_t *cmd, c.add_option(opt); } -/** - Remove all completion options in the specified entry that match the - 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(const wcstring &option, complete_option_type_t type) -{ +/// Remove all completion options in the specified entry that match the 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(const wcstring &option, complete_option_type_t type) { ASSERT_IS_LOCKED(completion_lock); option_list_t::iterator iter = this->options.begin(); - while (iter != this->options.end()) - { - if (iter->option == option && iter->type == type) - { + while (iter != this->options.end()) { + if (iter->option == option && iter->type == type) { iter = this->options.erase(iter); - } - else - { - /* Just go to the next one */ + } else { + // Just go to the next one. ++iter; } } return this->options.empty(); } - -void complete_remove(const wcstring &cmd, bool cmd_is_path, const wcstring &option, complete_option_type_t type) -{ +void complete_remove(const wcstring &cmd, bool cmd_is_path, const wcstring &option, + complete_option_type_t type) { scoped_lock lock(completion_lock); completion_entry_t tmp_entry(cmd, cmd_is_path, false); completion_entry_set_t::iterator iter = completion_set.find(tmp_entry); - if (iter != completion_set.end()) - { - // const_cast: See SET_ELEMENTS_ARE_IMMUTABLE - completion_entry_t &entry = const_cast(*iter); + if (iter != completion_set.end()) { + // const_cast: See SET_ELEMENTS_ARE_IMMUTABLE. + completion_entry_t &entry = const_cast(*iter); bool delete_it = entry.remove_option(option, type); - if (delete_it) - { - /* Delete this entry */ + if (delete_it) { + // Delete this entry. completion_set.erase(iter); } } } -void complete_remove_all(const wcstring &cmd, bool cmd_is_path) -{ +void complete_remove_all(const wcstring &cmd, bool cmd_is_path) { scoped_lock lock(completion_lock); - + completion_entry_t tmp_entry(cmd, cmd_is_path, false); completion_set.erase(tmp_entry); } - -/** - 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. */ +/// 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. path = L""; } - /* Make sure the path is not included in the command */ + // 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; } } -/** - Copy any strings in possible_comp which have the specified prefix - to the completer's completion array. The prefix may contain wildcards. - The output will consist of completion_t structs. - - There are three ways to specify descriptions for each - completion. Firstly, if a description has already been added to the - completion, it is _not_ replaced. Secondly, if the desc_func - function is specified, use it to determine a dynamic - completion. Thirdly, if none of the above are available, the desc - string is used as a description. - - \param wc_escaped the prefix, possibly containing wildcards. The wildcard should not have been unescaped, i.e. '*' should be used for any string, not the ANY_STRING character. - \param desc the default description, used for completions with no embedded description. The description _may_ contain a COMPLETE_SEP character, if not, one will be prefixed to it - \param desc_func the function that generates a description for those completions witout an embedded description - \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 &), +/// Copy any strings in possible_comp which have the specified prefix to the completer's completion +/// array. The prefix may contain wildcards. The output will consist of completion_t structs. +/// +/// There are three ways to specify descriptions for each completion. Firstly, if a description has +/// already been added to the completion, it is _not_ replaced. Secondly, if the desc_func function +/// is specified, use it to determine a dynamic completion. Thirdly, if none of the above are +/// available, the desc string is used as a description. +/// +/// \param wc_escaped the prefix, possibly containing wildcards. The wildcard should not have been +/// unescaped, i.e. '*' should be used for any string, not the ANY_STRING character. +/// \param desc the default description, used for completions with no embedded description. The +/// description _may_ contain a COMPLETE_SEP character, if not, one will be prefixed to it +/// \param desc_func the function that generates a description for those completions witout an +/// embedded description +/// \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) -{ + complete_flags_t flags) { wcstring tmp = wc_escaped; - if (! expand_one(tmp, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_WILDCARDS | this->expand_flags(), NULL)) + if (!expand_one(tmp, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_WILDCARDS | this->expand_flags(), NULL)) return; const wcstring wc = parse_util_unescape_wildcards(tmp); - for (size_t i=0; i< possible_comp.size(); i++) - { + 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(); + const wchar_t *next_str = temp.empty() ? NULL : temp.c_str(); - if (next_str) - { - wildcard_complete(next_str, wc.c_str(), desc, desc_func, &this->completions, this->expand_flags(), flags); + if (next_str) { + wildcard_complete(next_str, wc.c_str(), desc, desc_func, &this->completions, + this->expand_flags(), flags); } } } -/** - 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) -{ +/// 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) { ASSERT_IS_MAIN_THREAD(); const wchar_t *cmd_start; int skip; - - const wchar_t * const cmd = str.c_str(); - cmd_start=wcsrchr(cmd, L'/'); + const wchar_t *const cmd = str.c_str(); + cmd_start = wcsrchr(cmd, L'/'); 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)) - { + if (wildcard_has(cmd_start, 0)) { return; } skip = 1; - for (size_t i=0; i< this->completions.size(); i++) - { + 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 (c.completion.empty() || (c.completion[c.completion.size() - 1] != L'/')) { skip = 0; break; } - } - if (skip) - { + if (skip) { return; } - wcstring lookup_cmd(L"__fish_describe_command "); lookup_cmd.append(escape_string(cmd_start, 1)); std::map lookup; - /* - First locate a list of possible descriptions using a single - call to apropos or a direct search if we know the location - of the whatis database. This can take some time on slower - systems with a large set of manuals, but it should be ok - since apropos is only called once. - */ + // First locate a list of possible descriptions using a single call to apropos or a direct + // search if we know the location of the whatis database. This can take some time on slower + // systems with a large set of manuals, but it should be ok since apropos is only called once. wcstring_list_t list; - if (exec_subshell(lookup_cmd, list, false /* don't apply exit status */) != -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++) - { + if (exec_subshell(lookup_cmd, list, false /* don't apply exit status */) != -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. - */ - if (! val.empty()) - val[0]=towupper(val[0]); - + // 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. - - 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++) - { + // 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; i < this->completions.size(); i++) { completion_t &completion = this->completions.at(i); const wcstring &el = completion.completion; - if (el.empty()) - continue; + if (el.empty()) continue; std::map::iterator new_desc_iter = lookup.find(el); - if (new_desc_iter != lookup.end()) - completion.description = new_desc_iter->second; + 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) -{ +/// Returns a description for the specified function, or an empty string if none. +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; } - -/** - Complete the specified command name. Search for executables in the - path, executables defined using an absolute path, functions, - builtins and directories for implicit cd commands. - - \param cmd the command string to find completions for - - \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, bool use_implicit_cd) -{ - /* Paranoia */ - if (str_cmd.empty()) - return; +/// Complete the specified command name. Search for executables in the path, executables defined +/// using an absolute path, functions, builtins and directories for implicit cd commands. +/// +/// \param cmd the command string to find completions for +/// \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, bool use_implicit_cd) { + if (str_cmd.empty()) return; std::vector possible_comp; - if (use_command) - { - if (expand_string(str_cmd, &this->completions, EXPAND_SPECIAL_FOR_COMMAND | EXPAND_FOR_COMPLETIONS | EXECUTABLES_ONLY | this->expand_flags(), NULL) != EXPAND_ERROR) - { - if (this->wants_descriptions()) - { + if (use_command) { + if (expand_string(str_cmd, &this->completions, + EXPAND_SPECIAL_FOR_COMMAND | EXPAND_FOR_COMPLETIONS | EXECUTABLES_ONLY | + this->expand_flags(), + NULL) != EXPAND_ERROR) { + if (this->wants_descriptions()) { this->complete_cmd_desc(str_cmd); } } } - if (use_implicit_cd) - { - if (!expand_string(str_cmd, &this->completions, EXPAND_FOR_COMPLETIONS | DIRECTORIES_ONLY | this->expand_flags(), NULL)) - { + if (use_implicit_cd) { + if (!expand_string(str_cmd, &this->completions, + EXPAND_FOR_COMPLETIONS | DIRECTORIES_ONLY | this->expand_flags(), + NULL)) { // Not valid as implicit cd. } } - if (str_cmd.find(L'/') == wcstring::npos && str_cmd.at(0) != L'~') - { - if (use_function) - { + if (str_cmd.find(L'/') == wcstring::npos && str_cmd.at(0) != L'~') { + if (use_function) { wcstring_list_t names = function_get_names(str_cmd.at(0) == L'_'); - for (size_t i=0; i < names.size(); i++) - { + for (size_t i = 0; i < names.size(); i++) { append_completion(&possible_comp, names.at(i)); } @@ -879,113 +687,88 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool possible_comp.clear(); - if (use_builtin) - { + if (use_builtin) { builtin_get_names(&possible_comp); this->complete_strings(str_cmd, 0, &builtin_get_desc, possible_comp, 0); } - } } - -/** - Evaluate the argument list (as supplied by complete -a) and insert - any return matching completions. Matching is done using \c - copy_strings_with_prefix, meaning the completion may contain - wildcards. Logically, this is not always the right thing to do, but - I have yet to come up with a case where this matters. - - \param str The string to complete. - \param args The list of option arguments to be evaluated. - \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) -{ +/// Evaluate the argument list (as supplied by complete -a) and insert any return matching +/// completions. Matching is done using \c copy_strings_with_prefix, meaning the completion may +/// contain wildcards. Logically, this is not always the right thing to do, but I have yet to come +/// up with a case where this matters. +/// +/// \param str The string to complete. +/// \param args The list of option arguments to be evaluated. +/// \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) { bool is_autosuggest = (this->type() == COMPLETE_AUTOSUGGEST); - /* If type is COMPLETE_AUTOSUGGEST, it means we're on a background thread, so don't call proc_push_interactive */ - if (! is_autosuggest) - { + // If type is COMPLETE_AUTOSUGGEST, it means we're on a background thread, so don't call + // proc_push_interactive. + if (!is_autosuggest) { proc_push_interactive(0); } expand_flags_t eflags = 0; - if (is_autosuggest) - { + if (is_autosuggest) { eflags |= EXPAND_NO_DESCRIPTIONS | EXPAND_SKIP_CMDSUBST; } - + std::vector possible_comp; parser_t::expand_argument_list(args, eflags, &possible_comp); - if (! is_autosuggest) - { + if (!is_autosuggest) { proc_pop_interactive(); } this->complete_strings(escape_string(str, ESCAPE_ALL), desc.c_str(), 0, possible_comp, flags); } - -static size_t leading_dash_count(const wchar_t *str) -{ +static size_t leading_dash_count(const wchar_t *str) { size_t cursor = 0; - while (str[cursor] == L'-') - { + while (str[cursor] == L'-') { cursor++; } return cursor; } -/** - Match a parameter -*/ -static bool param_match(const complete_entry_opt_t *e, const wchar_t *optstr) -{ +/// Match a parameter. +static bool param_match(const complete_entry_opt_t *e, const wchar_t *optstr) { bool result = false; - if (e->type != option_type_args_only) - { + if (e->type != option_type_args_only) { size_t dashes = leading_dash_count(optstr); result = (dashes == e->expected_dash_count() && e->option == &optstr[dashes]); } return result; } -/** - Test if a string is an option with an argument, like --color=auto or -I/usr/include -*/ -static const wchar_t *param_match2(const complete_entry_opt_t *e, const wchar_t *optstr) -{ - // We may get a complete_entry_opt_t with no options if it's just arguments - if (e->option.empty()) - { +/// Test if a string is an option with an argument, like --color=auto or -I/usr/include. +static const wchar_t *param_match2(const complete_entry_opt_t *e, const wchar_t *optstr) { + // We may get a complete_entry_opt_t with no options if it's just arguments. + if (e->option.empty()) { return NULL; } - /* Verify leading dashes */ + // Verify leading dashes. size_t cursor = leading_dash_count(optstr); - if (cursor != e->expected_dash_count()) - { + if (cursor != e->expected_dash_count()) { return NULL; } - - /* Verify options match */ - if (! string_prefixes_string(e->option, &optstr[cursor])) - { + + // Verify options match. + if (!string_prefixes_string(e->option, &optstr[cursor])) { return NULL; } cursor += e->option.length(); - - /* short options are like -DNDEBUG. Long options are like --color=auto. So check for an equal sign for long options. */ - if (e->type != option_type_short) - { - if (optstr[cursor] != L'=') - { + + // Short options are like -DNDEBUG. Long options are like --color=auto. So check for an equal + // sign for long options. + if (e->type != option_type_short) { + if (optstr[cursor] != L'=') { return NULL; } cursor += 1; @@ -993,54 +776,44 @@ static const wchar_t *param_match2(const complete_entry_opt_t *e, const wchar_t return &optstr[cursor]; } -/** - Tests whether a short option is a viable completion. - arg_str will be like '-xzv', nextopt will be a character like 'f' - options will be the list of all options, used to validate the argument. -*/ -static bool short_ok(const wcstring &arg, const complete_entry_opt_t *entry, const option_list_t &options) -{ - /* Ensure it's a short option */ - if (entry->type != option_type_short || entry->option.empty()) - { +/// Tests whether a short option is a viable completion. arg_str will be like '-xzv', nextopt will +/// be a character like 'f' options will be the list of all options, used to validate the argument. +static bool short_ok(const wcstring &arg, const complete_entry_opt_t *entry, + const option_list_t &options) { + // Ensure it's a short option. + if (entry->type != option_type_short || entry->option.empty()) { return false; } const wchar_t nextopt = entry->option.at(0); - - /* Empty strings are always 'OK' */ - if (arg.empty()) - { + + // Empty strings are always 'OK'. + if (arg.empty()) { return true; } - - /* The argument must start with exactly one dash */ - if (leading_dash_count(arg.c_str()) != 1) - { + + // The argument must start with exactly one dash. + if (leading_dash_count(arg.c_str()) != 1) { return false; } - - /* Short option must not be already present */ - if (arg.find(nextopt) != wcstring::npos) - { + + // Short option must not be already present. + if (arg.find(nextopt) != wcstring::npos) { return false; } - - /* Verify that all characters in our combined short option list are present as short options in the options list. If we get a short option that can't be combined (NO_COMMON), then we stop. */ + + // Verify that all characters in our combined short option list are present as short options in + // the options list. If we get a short option that can't be combined (NO_COMMON), then we stop. bool result = true; - for (size_t i=1; i < arg.size(); i++) - { + for (size_t i = 1; i < arg.size(); i++) { wchar_t arg_char = arg.at(i); const complete_entry_opt_t *match = NULL; - for (option_list_t::const_iterator iter = options.begin(); iter != options.end(); ++iter) - { - if (iter->type == option_type_short && iter->option.at(0) == arg_char) - { + for (option_list_t::const_iterator iter = options.begin(); iter != options.end(); ++iter) { + if (iter->type == option_type_short && iter->option.at(0) == arg_char) { match = &*iter; break; } } - if (match == NULL || (match->result_mode & NO_COMMON)) - { + if (match == NULL || (match->result_mode & NO_COMMON)) { result = false; break; } @@ -1048,119 +821,97 @@ static bool short_ok(const wcstring &arg, const complete_entry_opt_t *entry, con return result; } - -/* Load command-specific completions for the specified command. */ -static void complete_load(const wcstring &name, bool reload) -{ - // we have to load this as a function, since it may define a --wraps or signature - // see #2466 +// Load command-specific completions for the specified command. +static void complete_load(const wcstring &name, bool reload) { + // We have to load this as a function, since it may define a --wraps or signature. + // See issue #2466. function_load(name); completion_autoloader.load(name, reload); } // Performed on main thread, from background thread. Return type is ignored. -static int complete_load_no_reload(wcstring *name) -{ +static int complete_load_no_reload(wcstring *name) { assert(name != NULL); ASSERT_IS_MAIN_THREAD(); complete_load(*name, false); return 0; } -/** - complete_param: Given a command, find completions for the argument str of command cmd_orig with previous option popt. - - Examples in format (cmd, popt, str): - - echo hello world -> ("echo", "world", "") - echo hello world -> ("echo", "hello", "world") - - Insert results into comp_out. Return true to perform file completion, false to disable it. -*/ -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 wchar_t * const popt = spopt.c_str(); - const wchar_t * const str = sstr.c_str(); +/// complete_param: Given a command, find completions for the argument str of command cmd_orig with +/// previous option popt. +/// +/// Examples in format (cmd, popt, str): +/// +/// echo hello world -> ("echo", "world", "") +/// echo hello world -> ("echo", "hello", "world") +/// +/// Insert results into comp_out. Return true to perform file completion, false to disable it. +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 wchar_t *const popt = spopt.c_str(); + const wchar_t *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) - { + if (this->type() == COMPLETE_DEFAULT) { ASSERT_IS_MAIN_THREAD(); complete_load(cmd, true); - } - else if (this->type() == COMPLETE_AUTOSUGGEST) - { - /* Maybe load this command (on the main thread) */ - if (! completion_autoloader.has_tried_loading(cmd)) - { + } else if (this->type() == COMPLETE_AUTOSUGGEST) { + // Maybe load this command (on the main thread). + if (!completion_autoloader.has_tried_loading(cmd)) { iothread_perform_on_main(complete_load_no_reload, &cmd); } } - /* Make a list of lists of all options that we care about */ + // Make a list of lists of all options that we care about. std::vector all_options; { scoped_lock lock(completion_lock); - for (completion_entry_set_t::const_iterator iter = completion_set.begin(); iter != completion_set.end(); ++iter) - { + 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)) - { - /* Copy all of their options into our list */ - all_options.push_back(i.get_options()); //Oof, this is a lot of copying + if (wildcard_match(match, i.cmd)) { + // Copy all of their options into our list. + all_options.push_back(i.get_options()); // Oof, this is a lot of copying } } } - /* Now release the lock and test each option that we captured above. - We have to do this outside the lock because callouts (like the condition) may add or remove completions. - See https://github.com/ridiculousfish/fishfish/issues/2 */ - for (std::vector::const_iterator iter = all_options.begin(); iter != all_options.end(); ++iter) - { + // Now release the lock and test each option that we captured above. We have to do this outside + // the lock because callouts (like the condition) may add or remove completions. See issue 2. + for (std::vector::const_iterator iter = all_options.begin(); + iter != all_options.end(); ++iter) { const option_list_t &options = *iter; - 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) { + 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; const wchar_t *arg = param_match2(o, str); - if (arg != NULL && this->condition_test(o->condition)) - { + if (arg != NULL && 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); } - } - } - else if (popt[0] == L'-') - { - /* Set to true if we found a matching old-style switch */ + } else if (popt[0] == L'-') { + // Set to true if we found a matching old-style switch. bool old_style_match = false; - /* - 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) - { + // 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->type == option_type_single_long) - { - if (param_match(o, popt) && this->condition_test(o->condition)) - { + if (o->type == option_type_single_long) { + if (param_match(o, popt) && this->condition_test(o->condition)) { old_style_match = true; if (o->result_mode & NO_COMMON) use_common = false; if (o->result_mode & NO_FILES) use_files = false; @@ -1169,129 +920,88 @@ bool completer_t::complete_param(const wcstring &scmd_orig, const wcstring &spop } } - /* - 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) - { + // 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. - */ + // 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->type == option_type_double_long && !(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, o->localized_desc(), o->flags); - } } } } } - if (use_common) - { - - for (option_list_t::const_iterator oiter = options.begin(); oiter != options.end(); ++oiter) - { + 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->condition_test(o->condition)) - continue; - if (o->option.empty()) - { - use_files = use_files && ((o->result_mode & NO_FILES)==0); + // If this entry is for the base command, check if any of the arguments match. + if (!this->condition_test(o->condition)) continue; + if (o->option.empty()) { + use_files = 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 (short_ok(str, o, options)) - { - // It's a match + if (wcslen(str) > 0 && use_switches) { + // Check if the short style option matches. + if (short_ok(str, o, options)) { + // It's a match. const wcstring desc = o->localized_desc(); append_completion(&this->completions, o->option, desc, 0); - } - /* - Check if the long style option matches - */ - if (o->type == option_type_single_long || o->type == option_type_double_long) - { - int match=0, match_no_case=0; + // Check if the long style option matches. + if (o->type == option_type_single_long || o->type == option_type_double_long) { + int match = 0, match_no_case = 0; wcstring whole_opt(o->expected_dash_count(), L'-'); whole_opt.append(o->option); 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 */ + 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) - { + if (match) { offset = wcslen(str); - } - else - { + } else { flags = COMPLETE_REPLACES_TOKEN; } - has_arg = ! o->comp.empty(); + has_arg = !o->comp.empty(); req_arg = (o->result_mode & NO_COMMON); - if (o->type == option_type_double_long && (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), + if (o->type == option_type_double_long && (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), flags); - } - append_completion(&this->completions, - whole_opt.c_str() + offset, - C_(o->desc), - flags); + append_completion(&this->completions, whole_opt.c_str() + offset, + C_(o->desc), flags); } } } @@ -1302,127 +1012,100 @@ bool completer_t::complete_param(const wcstring &scmd_orig, const wcstring &spop return use_files; } -/** - Perform generic (not command-specific) expansions on the specified string -*/ -void completer_t::complete_param_expand(const wcstring &str, bool do_file, bool handle_as_special_cd) -{ +/// Perform generic (not command-specific) expansions on the specified string. +void completer_t::complete_param_expand(const wcstring &str, bool do_file, + bool handle_as_special_cd) { expand_flags_t flags = EXPAND_SKIP_CMDSUBST | EXPAND_FOR_COMPLETIONS | this->expand_flags(); - if (! do_file) - flags |= EXPAND_SKIP_WILDCARDS; - - if (handle_as_special_cd && do_file) - { + if (!do_file) flags |= EXPAND_SKIP_WILDCARDS; + + if (handle_as_special_cd && do_file) { flags |= DIRECTORIES_ONLY | EXPAND_SPECIAL_FOR_CD | EXPAND_NO_DESCRIPTIONS; } - /* Squelch file descriptions per issue 254 */ - if (this->type() == COMPLETE_AUTOSUGGEST || do_file) - flags |= EXPAND_NO_DESCRIPTIONS; + // Squelch file descriptions per issue #254. + if (this->type() == COMPLETE_AUTOSUGGEST || do_file) flags |= EXPAND_NO_DESCRIPTIONS; - /* We have the following cases: - - --foo=bar => expand just bar - -foo=bar => expand just bar - foo=bar => expand the whole thing, and also just bar - - We also support colon separator (#2178). If there's more than one, prefer the last one. - */ - + // We have the following cases: + // + // --foo=bar => expand just bar + // -foo=bar => expand just bar + // foo=bar => expand the whole thing, and also just bar + // + // We also support colon separator (#2178). If there's more than one, prefer the last one. size_t sep_index = str.find_last_of(L"=:"); bool complete_from_separator = (sep_index != wcstring::npos); bool complete_from_start = !complete_from_separator || !string_prefixes_string(L"-", str); - - if (complete_from_separator) - { + + if (complete_from_separator) { const wcstring sep_string = wcstring(str, sep_index + 1); std::vector local_completions; - if (expand_string(sep_string, - &local_completions, - flags, - NULL) == EXPAND_ERROR) - { + if (expand_string(sep_string, &local_completions, flags, NULL) == EXPAND_ERROR) { debug(3, L"Error while expanding string '%ls'", sep_string.c_str()); } - - /* Any COMPLETE_REPLACES_TOKEN will also stomp the separator. We need to "repair" them by inserting our separator and prefix. */ + + // Any COMPLETE_REPLACES_TOKEN will also stomp the separator. We need to "repair" them by + // inserting our separator and prefix. const wcstring prefix_with_sep = wcstring(str, 0, sep_index + 1); - for (size_t i=0; i < local_completions.size(); i++) - { + for (size_t i = 0; i < local_completions.size(); i++) { local_completions.at(i).prepend_token_prefix(prefix_with_sep); } - this->completions.insert(this->completions.end(), - local_completions.begin(), + this->completions.insert(this->completions.end(), local_completions.begin(), local_completions.end()); } - - if (complete_from_start) - { - /* Don't do fuzzy matching for files if the string begins with a dash (#568). We could consider relaxing this if there was a preceding double-dash argument */ - if (string_prefixes_string(L"-", str)) - flags &= ~EXPAND_FUZZY_MATCH; - - if (expand_string(str, - &this->completions, - flags, NULL) == EXPAND_ERROR) - { + + if (complete_from_start) { + // Don't do fuzzy matching for files if the string begins with a dash (issue #568). We could + // consider relaxing this if there was a preceding double-dash argument. + if (string_prefixes_string(L"-", str)) flags &= ~EXPAND_FUZZY_MATCH; + + if (expand_string(str, &this->completions, flags, NULL) == EXPAND_ERROR) { debug(3, L"Error while expanding string '%ls'", str.c_str()); } } } -/** - Complete the specified string as an environment variable -*/ -bool completer_t::complete_variable(const wcstring &str, size_t start_offset) -{ - const wchar_t * const whole_var = str.c_str(); +/// Complete the specified string as an environment variable. +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); bool res = false; const wcstring_list_t names = complete_get_variable_names(); - for (size_t i=0; imax_fuzzy_match_type()); - if (match.type == fuzzy_match_none) - { - // No match - continue; + string_fuzzy_match_t match = + string_fuzzy_match_string(var, env_name, this->max_fuzzy_match_type()); + if (match.type == fuzzy_match_none) { + continue; // no match } wcstring comp; int flags = 0; - if (! match_type_requires_full_replacement(match.type)) - { - // Take only the suffix + if (!match_type_requires_full_replacement(match.type)) { + // Take only the suffix. comp.append(env_name.c_str() + varlen); - } - else - { + } else { comp.append(whole_var, start_offset); comp.append(env_name); flags = COMPLETE_REPLACES_TOKEN | COMPLETE_DONT_ESCAPE; } wcstring desc; - if (this->wants_descriptions()) - { - // Can't use this->vars here, it could be any variable + if (this->wants_descriptions()) { + // Can't use this->vars here, it could be any variable. env_var_t value_unescaped = env_get_string(env_name); - if (value_unescaped.missing()) - continue; + if (value_unescaped.missing()) continue; wcstring value = expand_escape_variable(value_unescaped); if (this->type() != COMPLETE_AUTOSUGGEST) desc = format_string(COMPLETE_VAR_DESC_VAL, value.c_str()); } - append_completion(&this->completions, comp, desc, flags, match); + append_completion(&this->completions, comp, desc, flags, match); res = true; } @@ -1430,128 +1113,103 @@ bool completer_t::complete_variable(const wcstring &str, size_t start_offset) return res; } -bool completer_t::try_complete_variable(const wcstring &str) -{ - enum {e_unquoted, e_single_quoted, e_double_quoted} mode = e_unquoted; +bool completer_t::try_complete_variable(const wcstring &str) { + enum { e_unquoted, e_single_quoted, e_double_quoted } mode = e_unquoted; const size_t len = str.size(); - /* Get the position of the dollar heading a (possibly empty) run of valid variable characters. npos means none. */ + // Get the position of the dollar heading a (possibly empty) run of valid variable characters. + // npos means none. size_t variable_start = wcstring::npos; - for (size_t in_pos=0; in_posflags & COMPLETION_REQUEST_AUTOSUGGESTION); + // Now complete if we have a variable start. Note the variable text may be empty; in that case + // don't generate an autosuggestion, but do allow tab completion. + bool allow_empty = !(this->flags & COMPLETION_REQUEST_AUTOSUGGESTION); bool text_is_empty = (variable_start == len); bool result = false; - if (variable_start != wcstring::npos && (allow_empty || ! text_is_empty)) - { + if (variable_start != wcstring::npos && (allow_empty || !text_is_empty)) { result = this->complete_variable(str, variable_start + 1); } return result; } -/** - Try to complete the specified string as a username. This is used by - ~USER type expansion. - - \return 0 if unable to complete, 1 otherwise -*/ -bool completer_t::try_complete_user(const wcstring &str) -{ +/// Try to complete the specified string as a username. This is used by ~USER type expansion. +/// +/// \return 0 if unable to complete, 1 otherwise +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; + 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; + 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 (name_end == 0) { struct passwd *pw; size_t name_len = wcslen(user_name); setpwent(); - while ((pw=getpwent()) != 0) - { + while ((pw = getpwent()) != 0) { double current_time = timef(); - if (current_time - start_time > 0.2) - { + if (current_time - start_time > 0.2) { return 1; } - if (pw->pw_name) - { + if (pw->pw_name) { const wcstring pw_name_str = str2wcstring(pw->pw_name); const wchar_t *pw_name = pw_name_str.c_str(); - if (wcsncmp(user_name, pw_name, name_len)==0) - { + 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, + append_completion(&this->completions, &pw_name[name_len], desc, COMPLETE_NO_SPACE); - res=1; - } - else if (wcsncasecmp(user_name, pw_name, name_len)==0) - { + 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_REPLACES_TOKEN | COMPLETE_DONT_ESCAPE | COMPLETE_NO_SPACE); - res=1; + append_completion(&this->completions, name, desc, COMPLETE_REPLACES_TOKEN | + COMPLETE_DONT_ESCAPE | + COMPLETE_NO_SPACE); + res = 1; } } } @@ -1562,76 +1220,83 @@ bool completer_t::try_complete_user(const wcstring &str) return res; } -void complete(const wcstring &cmd_with_subcmds, std::vector *out_comps, completion_request_flags_t flags, const env_vars_snapshot_t &vars) -{ - /* Determine the innermost subcommand */ +void complete(const wcstring &cmd_with_subcmds, std::vector *out_comps, + completion_request_flags_t flags, const env_vars_snapshot_t &vars) { + // Determine the innermost subcommand. const wchar_t *cmdsubst_begin, *cmdsubst_end; - parse_util_cmdsubst_extent(cmd_with_subcmds.c_str(), cmd_with_subcmds.size(), &cmdsubst_begin, &cmdsubst_end); + parse_util_cmdsubst_extent(cmd_with_subcmds.c_str(), cmd_with_subcmds.size(), &cmdsubst_begin, + &cmdsubst_end); assert(cmdsubst_begin != NULL && cmdsubst_end != NULL && cmdsubst_end >= cmdsubst_begin); const wcstring cmd = wcstring(cmdsubst_begin, cmdsubst_end - cmdsubst_begin); - /* Make our completer */ + // Make our completer. completer_t completer(cmd, flags, vars); wcstring current_command; const size_t pos = cmd.size(); - bool done=false; + bool done = false; bool use_command = 1; bool use_function = 1; bool use_builtin = 1; bool use_implicit_cd = 1; - //debug( 1, L"Complete '%ls'", cmd.c_str() ); + // debug( 1, L"Complete '%ls'", cmd.c_str() ); const wchar_t *cmd_cstr = cmd.c_str(); const wchar_t *tok_begin = NULL, *prev_begin = NULL, *prev_end = NULL; parse_util_token_extent(cmd_cstr, cmd.size(), &tok_begin, NULL, &prev_begin, &prev_end); - /** - If we are completing a variable name or a tilde expansion user - name, we do that and return. No need for any other completions. - */ + // If we are completing a variable name or a tilde expansion user name, we do that and return. + // No need for any other completions. const wcstring current_token = tok_begin; - /* Unconditionally complete variables and processes. This is a little weird since we will happily complete variables even in e.g. command position, despite the fact that they are invalid there. */ - if (!done) - { - done = completer.try_complete_variable(current_token) || completer.try_complete_user(current_token); + // Unconditionally complete variables and processes. This is a little weird since we will + // happily complete variables even in e.g. command position, despite the fact that they are + // invalid there. */ + if (!done) { + done = completer.try_complete_variable(current_token) || + completer.try_complete_user(current_token); } - if (!done) - { + if (!done) { parse_node_tree_t tree; - parse_tree_from_string(cmd, parse_flag_continue_after_error | parse_flag_accept_incomplete_tokens | parse_flag_include_comments, &tree, NULL); + parse_tree_from_string(cmd, parse_flag_continue_after_error | + parse_flag_accept_incomplete_tokens | + parse_flag_include_comments, + &tree, NULL); - /* Find any plain statement that contains the position. We have to backtrack past spaces (#1261). So this will be at either the last space character, or after the end of the string */ + // Find any plain statement that contains the position. We have to backtrack past spaces + // (issue #1261). So this will be at either the last space character, or after the end of + // the string. size_t adjusted_pos = pos; - while (adjusted_pos > 0 && cmd.at(adjusted_pos - 1) == L' ') - { + while (adjusted_pos > 0 && cmd.at(adjusted_pos - 1) == L' ') { adjusted_pos--; } - const parse_node_t *plain_statement = tree.find_node_matching_source_location(symbol_plain_statement, adjusted_pos, NULL); - if (plain_statement == NULL) - { - /* Not part of a plain statement. This could be e.g. a for loop header, case expression, etc. Do generic file completions (#1309). If we had to backtrack, it means there was whitespace; don't do an autosuggestion in that case. Also don't do it if we are just after a pipe, semicolon, or & (#1631), or in a comment. - - Overall this logic is a total mess. A better approach would be to return the "possible next token" from the parse tree directly (this data is available as the first of the sequence of nodes without source locations at the very end of the parse tree). */ + const parse_node_t *plain_statement = + tree.find_node_matching_source_location(symbol_plain_statement, adjusted_pos, NULL); + if (plain_statement == NULL) { + // Not part of a plain statement. This could be e.g. a for loop header, case expression, + // etc. Do generic file completions (issue #1309). If we had to backtrack, it means + // there was whitespace; don't do an autosuggestion in that case. Also don't do it if we + // are just after a pipe, semicolon, or & (issue #1631), or in a comment. + // + // Overall this logic is a total mess. A better approach would be to return the + // "possible next token" from the parse tree directly (this data is available as the + // first of the sequence of nodes without source locations at the very end of the parse + // tree). bool do_file = true; - if (flags & COMPLETION_REQUEST_AUTOSUGGESTION) - { - if (adjusted_pos < pos) - { + if (flags & COMPLETION_REQUEST_AUTOSUGGESTION) { + if (adjusted_pos < pos) { do_file = false; - } - else if (pos > 0) - { - // If the previous character is in one of these types, we don't do file suggestions - parse_token_type_t bad_types[] = {parse_token_type_pipe, parse_token_type_end, parse_token_type_background, parse_special_type_comment}; - for (size_t i=0; i < sizeof bad_types / sizeof *bad_types; i++) - { - if (tree.find_node_matching_source_location(bad_types[i], pos - 1, NULL)) - { + } else if (pos > 0) { + // If the previous character is in one of these types, we don't do file + // suggestions. + parse_token_type_t bad_types[] = {parse_token_type_pipe, parse_token_type_end, + parse_token_type_background, + parse_special_type_comment}; + for (size_t i = 0; i < sizeof bad_types / sizeof *bad_types; i++) { + if (tree.find_node_matching_source_location(bad_types[i], pos - 1, NULL)) { do_file = false; break; } @@ -1639,61 +1304,58 @@ void complete(const wcstring &cmd_with_subcmds, std::vector *out_c } } completer.complete_param_expand(current_token, do_file); - } - else - { - assert(plain_statement->has_source() && plain_statement->type == symbol_plain_statement); + } else { + assert(plain_statement->has_source() && + plain_statement->type == symbol_plain_statement); - /* Get the command node */ - const parse_node_t *cmd_node = tree.get_child(*plain_statement, 0, parse_token_type_string); + // Get the command node. + const parse_node_t *cmd_node = + tree.get_child(*plain_statement, 0, parse_token_type_string); - /* Get the actual command string */ - if (cmd_node != NULL) - current_command = cmd_node->get_source(cmd); + // Get the actual command string. + if (cmd_node != NULL) current_command = cmd_node->get_source(cmd); - /* Check the decoration */ - switch (tree.decoration_for_plain_statement(*plain_statement)) - { - case parse_statement_decoration_none: + // Check the decoration. + switch (tree.decoration_for_plain_statement(*plain_statement)) { + case parse_statement_decoration_none: { use_command = true; use_function = true; use_builtin = true; use_implicit_cd = true; break; - + } case parse_statement_decoration_command: - case parse_statement_decoration_exec: + case parse_statement_decoration_exec: { use_command = true; use_function = false; use_builtin = false; use_implicit_cd = false; break; - - case parse_statement_decoration_builtin: + } + case parse_statement_decoration_builtin: { use_command = false; use_function = false; use_builtin = true; use_implicit_cd = false; break; + } } - if (cmd_node && cmd_node->location_in_or_at_end_of_source_range(pos)) - { - /* Complete command filename */ - completer.complete_cmd(current_token, use_function, use_builtin, use_command, use_implicit_cd); - } - else - { - /* Get all the arguments */ - const parse_node_tree_t::parse_node_list_t all_arguments = tree.find_nodes(*plain_statement, symbol_argument); + if (cmd_node && cmd_node->location_in_or_at_end_of_source_range(pos)) { + // Complete command filename. + completer.complete_cmd(current_token, use_function, use_builtin, use_command, + use_implicit_cd); + } else { + // Get all the arguments. + const parse_node_tree_t::parse_node_list_t all_arguments = + tree.find_nodes(*plain_statement, symbol_argument); - /* See whether we are in an argument. We may also be in a redirection, or nothing at all. */ + // See whether we are in an argument. We may also be in a redirection, or nothing at + // all. size_t matching_arg_index = -1; - for (size_t i=0; i < all_arguments.size(); i++) - { + for (size_t i = 0; i < all_arguments.size(); i++) { const parse_node_t *node = all_arguments.at(i); - if (node->location_in_or_at_end_of_source_range(adjusted_pos)) - { + if (node->location_in_or_at_end_of_source_range(adjusted_pos)) { matching_arg_index = i; break; } @@ -1701,103 +1363,103 @@ void complete(const wcstring &cmd_with_subcmds, std::vector *out_c bool had_ddash = false; wcstring current_argument, previous_argument; - if (matching_arg_index != (size_t)(-1)) - { - const wcstring matching_arg = all_arguments.at(matching_arg_index)->get_source(cmd); - - /* If the cursor is in whitespace, then the "current" argument is empty and the previous argument is the matching one. But if the cursor was in or at the end of the argument, then the current argument is the matching one, and the previous argument is the one before it. */ + if (matching_arg_index != (size_t)(-1)) { + const wcstring matching_arg = + all_arguments.at(matching_arg_index)->get_source(cmd); + + // If the cursor is in whitespace, then the "current" argument is empty and the + // previous argument is the matching one. But if the cursor was in or at the end + // of the argument, then the current argument is the matching one, and the + // previous argument is the one before it. bool cursor_in_whitespace = (adjusted_pos < pos); - if (cursor_in_whitespace) - { + if (cursor_in_whitespace) { current_argument = L""; previous_argument = matching_arg; - } - else - { + } else { current_argument = matching_arg; - if (matching_arg_index > 0) - { - previous_argument = all_arguments.at(matching_arg_index - 1)->get_source(cmd); + if (matching_arg_index > 0) { + previous_argument = + all_arguments.at(matching_arg_index - 1)->get_source(cmd); } } - /* Check to see if we have a preceding double-dash */ - for (size_t i=0; i < matching_arg_index; i++) - { - if (all_arguments.at(i)->get_source(cmd) == L"--") - { + // Check to see if we have a preceding double-dash. + for (size_t i = 0; i < matching_arg_index; i++) { + if (all_arguments.at(i)->get_source(cmd) == L"--") { had_ddash = true; break; } } } - - /* If we are not in an argument, we may be in a redirection */ + + // If we are not in an argument, we may be in a redirection. bool in_redirection = false; - if (matching_arg_index == (size_t)(-1)) - { - const parse_node_t *redirection = tree.find_node_matching_source_location(symbol_redirection, adjusted_pos, plain_statement); + if (matching_arg_index == (size_t)(-1)) { + const parse_node_t *redirection = tree.find_node_matching_source_location( + symbol_redirection, adjusted_pos, plain_statement); in_redirection = (redirection != NULL); } - + bool do_file = false, handle_as_special_cd = false; - if (in_redirection) - { + if (in_redirection) { do_file = true; - } - else - { - /* Try completing as an argument */ - wcstring current_command_unescape, previous_argument_unescape, current_argument_unescape; - if (unescape_string(current_command, ¤t_command_unescape, UNESCAPE_DEFAULT) && - unescape_string(previous_argument, &previous_argument_unescape, UNESCAPE_DEFAULT) && - unescape_string(current_argument, ¤t_argument_unescape, UNESCAPE_INCOMPLETE)) - { - // Have to walk over the command and its entire wrap chain - // If any command disables do_file, then they all do + } else { + // Try completing as an argument. + wcstring current_command_unescape, previous_argument_unescape, + current_argument_unescape; + if (unescape_string(current_command, ¤t_command_unescape, + UNESCAPE_DEFAULT) && + unescape_string(previous_argument, &previous_argument_unescape, + UNESCAPE_DEFAULT) && + unescape_string(current_argument, ¤t_argument_unescape, + UNESCAPE_INCOMPLETE)) { + // Have to walk over the command and its entire wrap chain. If any command + // disables do_file, then they all do. do_file = true; - const wcstring_list_t wrap_chain = complete_get_wrap_chain(current_command_unescape); - for (size_t i=0; i < wrap_chain.size(); i++) - { - // Hackish, this. The first command in the chain is always the given command. For every command past the first, we need to create a transient commandline for builtin_commandline. But not for COMPLETION_REQUEST_AUTOSUGGESTION, which may occur on background threads. + const wcstring_list_t wrap_chain = + complete_get_wrap_chain(current_command_unescape); + for (size_t i = 0; i < wrap_chain.size(); i++) { + // Hackish, this. The first command in the chain is always the given + // command. For every command past the first, we need to create a + // transient commandline for builtin_commandline. But not for + // COMPLETION_REQUEST_AUTOSUGGESTION, which may occur on background + // threads. builtin_commandline_scoped_transient_t *transient_cmd = NULL; - if (i == 0) - { + if (i == 0) { assert(wrap_chain.at(i) == current_command_unescape); - } - else if (! (flags & COMPLETION_REQUEST_AUTOSUGGESTION)) - { + } else if (!(flags & COMPLETION_REQUEST_AUTOSUGGESTION)) { assert(cmd_node != NULL); wcstring faux_cmdline = cmd; - faux_cmdline.replace(cmd_node->source_start, cmd_node->source_length, wrap_chain.at(i)); - transient_cmd = new builtin_commandline_scoped_transient_t(faux_cmdline); + faux_cmdline.replace(cmd_node->source_start, + cmd_node->source_length, wrap_chain.at(i)); + transient_cmd = + new builtin_commandline_scoped_transient_t(faux_cmdline); } - if (! completer.complete_param(wrap_chain.at(i), - previous_argument_unescape, - current_argument_unescape, - !had_ddash)) - { + if (!completer.complete_param(wrap_chain.at(i), + previous_argument_unescape, + current_argument_unescape, !had_ddash)) { do_file = false; } - delete transient_cmd; //may be null + delete transient_cmd; // may be null } } - /* If we have found no command specific completions at all, fall back to using file completions. */ - if (completer.empty()) - do_file = true; - - /* Hack. If we're cd, handle it specially (#1059, others) */ + // If we have found no command specific completions at all, fall back to using + // file completions. + if (completer.empty()) do_file = true; + + // Hack. If we're cd, handle it specially (issue #1059, others). handle_as_special_cd = (current_command_unescape == L"cd"); - - /* And if we're autosuggesting, and the token is empty, don't do file suggestions */ - if ((flags & COMPLETION_REQUEST_AUTOSUGGESTION) && current_argument_unescape.empty()) - { + + // And if we're autosuggesting, and the token is empty, don't do file + // suggestions. + if ((flags & COMPLETION_REQUEST_AUTOSUGGESTION) && + current_argument_unescape.empty()) { do_file = false; } } - /* This function wants the unescaped string */ + // This function wants the unescaped string. completer.complete_param_expand(current_token, do_file, handle_as_special_cd); } } @@ -1806,100 +1468,72 @@ void complete(const wcstring &cmd_with_subcmds, std::vector *out_c *out_comps = completer.get_completions(); } - - -/** - Print the GNU longopt style switch \c opt, and the argument \c - 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) -{ - if (argument.empty()) - return; +/// Print the GNU longopt style switch \c opt, and the argument \c 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) { + if (argument.empty()) return; wcstring esc = escape_string(argument, 1); append_format(out, L" --%ls %ls", opt.c_str(), esc.c_str()); } -wcstring complete_print() -{ +wcstring complete_print() { wcstring out; scoped_lock locker(completion_lock); - // Get a list of all completions in a vector, then sort it by order + // Get a list of all completions in a vector, then sort it by order. std::vector all_completions; - for (completion_entry_set_t::const_iterator i = completion_set.begin(); i != completion_set.end(); ++i) - { + for (completion_entry_set_t::const_iterator i = completion_set.begin(); + i != completion_set.end(); ++i) { all_completions.push_back(&*i); } sort(all_completions.begin(), all_completions.end(), compare_completions_by_order); - for (std::vector::const_iterator iter = all_completions.begin(); iter != all_completions.end(); ++iter) - { + for (std::vector::const_iterator iter = all_completions.begin(); + iter != all_completions.end(); ++iter) { const completion_entry_t *e = *iter; const option_list_t &options = e->get_options(); - for (option_list_t::const_iterator oiter = options.begin(); oiter != options.end(); ++oiter) - { + for (option_list_t::const_iterator oiter = options.begin(); oiter != options.end(); + ++oiter) { const complete_entry_opt_t *o = &*oiter; - const wchar_t *modestr[] = - { - L"", - L" --no-files", - L" --require-parameter", - L" --exclusive" - } - ; + const wchar_t *modestr[] = {L"", L" --no-files", L" --require-parameter", + L" --exclusive"}; - append_format(out, - L"complete%ls", - modestr[o->result_mode]); + append_format(out, L"complete%ls", modestr[o->result_mode]); - append_switch(out, - e->cmd_is_path ? L"path" : L"command", + append_switch(out, e->cmd_is_path ? L"path" : L"command", escape_string(e->cmd, ESCAPE_ALL)); - - switch (o->type) - { - case option_type_args_only: + + switch (o->type) { + case option_type_args_only: { break; - - case option_type_short: - assert(! o->option.empty()); + } + case option_type_short: { + assert(!o->option.empty()); append_format(out, L" --short-option '%lc'", o->option.at(0)); break; - + } case option_type_single_long: - case option_type_double_long: - append_switch(out, - o->type == option_type_single_long ? L"old-option" : L"long-option", - o->option); + case option_type_double_long: { + append_switch( + out, o->type == option_type_single_long ? L"old-option" : L"long-option", + o->option); break; + } } - append_switch(out, - L"description", - C_(o->desc)); - - append_switch(out, - L"arguments", - o->comp); - - append_switch(out, - L"condition", - o->condition); - + append_switch(out, L"description", C_(o->desc)); + append_switch(out, L"arguments", o->comp); + append_switch(out, L"condition", o->condition); out.append(L"\n"); } } - - /* Append wraps. This is a wonky interface where even values are the commands, and odd values are the targets that they wrap. */ + + // Append wraps. This is a wonky interface where even values are the commands, and odd values + // are the targets that they wrap. const wcstring_list_t wrap_pairs = complete_get_wrap_pairs(); assert(wrap_pairs.size() % 2 == 0); - for (size_t i=0; i < wrap_pairs.size(); ) - { + for (size_t i = 0; i < wrap_pairs.size();) { const wcstring &cmd = wrap_pairs.at(i++); const wcstring &target = wrap_pairs.at(i++); append_format(out, L"complete --command %ls --wraps %ls\n", cmd.c_str(), target.c_str()); @@ -1907,58 +1541,50 @@ wcstring complete_print() return out; } - -/* Completion "wrapper" support. The map goes from wrapping-command to wrapped-command-list */ +// Completion "wrapper" support. The map goes from wrapping-command to wrapped-command-list. static pthread_mutex_t wrapper_lock = PTHREAD_MUTEX_INITIALIZER; typedef std::map wrapper_map_t; -static wrapper_map_t &wrap_map() -{ +static wrapper_map_t &wrap_map() { ASSERT_IS_LOCKED(wrapper_lock); - // A pointer is a little more efficient than an object as a static because we can elide the thread-safe initialization + // A pointer is a little more efficient than an object as a static because we can elide the + // thread-safe initialization. static wrapper_map_t *wrapper_map = NULL; - if (wrapper_map == NULL) - { + if (wrapper_map == NULL) { wrapper_map = new wrapper_map_t(); } return *wrapper_map; } -/* Add a new target that is wrapped by command. Example: __fish_sgrep (command) wraps grep (target). */ -bool complete_add_wrapper(const wcstring &command, const wcstring &new_target) -{ - if (command.empty() || new_target.empty()) - { +// Add a new target that is wrapped by command. Example: __fish_sgrep (command) wraps grep (target). +bool complete_add_wrapper(const wcstring &command, const wcstring &new_target) { + if (command.empty() || new_target.empty()) { return false; } - + scoped_lock locker(wrapper_lock); wrapper_map_t &wraps = wrap_map(); wcstring_list_t *targets = &wraps[command]; - // If it's already present, we do nothing - if (std::find(targets->begin(), targets->end(), new_target) == targets->end()) - { + // If it's already present, we do nothing. + if (std::find(targets->begin(), targets->end(), new_target) == targets->end()) { targets->push_back(new_target); } return true; } -bool complete_remove_wrapper(const wcstring &command, const wcstring &target_to_remove) -{ - if (command.empty() || target_to_remove.empty()) - { +bool complete_remove_wrapper(const wcstring &command, const wcstring &target_to_remove) { + if (command.empty() || target_to_remove.empty()) { return false; } - + scoped_lock locker(wrapper_lock); wrapper_map_t &wraps = wrap_map(); bool result = false; wrapper_map_t::iterator current_targets_iter = wraps.find(command); - if (current_targets_iter != wraps.end()) - { + if (current_targets_iter != wraps.end()) { wcstring_list_t *targets = ¤t_targets_iter->second; - wcstring_list_t::iterator where = std::find(targets->begin(), targets->end(), target_to_remove); - if (where != targets->end()) - { + wcstring_list_t::iterator where = + std::find(targets->begin(), targets->end(), target_to_remove); + if (where != targets->end()) { targets->erase(where); result = true; } @@ -1966,58 +1592,52 @@ bool complete_remove_wrapper(const wcstring &command, const wcstring &target_to_ return result; } -wcstring_list_t complete_get_wrap_chain(const wcstring &command) -{ - if (command.empty()) - { +wcstring_list_t complete_get_wrap_chain(const wcstring &command) { + if (command.empty()) { return wcstring_list_t(); } scoped_lock locker(wrapper_lock); const wrapper_map_t &wraps = wrap_map(); - + wcstring_list_t result; - std::set visited; // set of visited commands - wcstring_list_t to_visit(1, command); // stack of remaining-to-visit commands - + std::set visited; // set of visited commands + wcstring_list_t to_visit(1, command); // stack of remaining-to-visit commands + wcstring target; - while (! to_visit.empty()) - { - // Grab the next command to visit, put it in target + while (!to_visit.empty()) { + // Grab the next command to visit, put it in target. target.swap(to_visit.back()); to_visit.pop_back(); - - // Try inserting into visited. If it was already present, we skip it; this is how we avoid loops. - if (! visited.insert(target).second) - { + + // Try inserting into visited. If it was already present, we skip it; this is how we avoid + // loops. + if (!visited.insert(target).second) { continue; } - - // Insert the target in the result. Note this is the command itself, if this is the first iteration of the loop. + + // Insert the target in the result. Note this is the command itself, if this is the first + // iteration of the loop. result.push_back(target); - - // Enqueue its children + + // Enqueue its children. wrapper_map_t::const_iterator target_children_iter = wraps.find(target); - if (target_children_iter != wraps.end()) - { + if (target_children_iter != wraps.end()) { const wcstring_list_t &children = target_children_iter->second; to_visit.insert(to_visit.end(), children.begin(), children.end()); } } - + return result; } -wcstring_list_t complete_get_wrap_pairs() -{ +wcstring_list_t complete_get_wrap_pairs() { wcstring_list_t result; scoped_lock locker(wrapper_lock); const wrapper_map_t &wraps = wrap_map(); - for (wrapper_map_t::const_iterator outer = wraps.begin(); outer != wraps.end(); ++outer) - { + for (wrapper_map_t::const_iterator outer = wraps.begin(); outer != wraps.end(); ++outer) { const wcstring &cmd = outer->first; const wcstring_list_t &targets = outer->second; - for (size_t i=0; i < targets.size(); i++) - { + for (size_t i = 0; i < targets.size(); i++) { result.push_back(cmd); result.push_back(targets.at(i)); } diff --git a/src/complete.h b/src/complete.h index 1e2c1a185..57c743c02 100644 --- a/src/complete.h +++ b/src/complete.h @@ -1,269 +1,197 @@ -/** \file complete.h - Prototypes for functions related to tab-completion. - - These functions are used for storing and retrieving tab-completion - data, as well as for performing tab-completion. -*/ - +/// Prototypes for functions related to tab-completion. +/// +/// These functions are used for storing and retrieving tab-completion data, as well as for +/// performing tab-completion. #ifndef FISH_COMPLETE_H #define FISH_COMPLETE_H -#include -#include #include +#include +#include #include "common.h" -/** - * Use all completions - */ +/// Use all completions. #define SHARED 0 -/** - * Do not use file completion - */ +/// Do not use file completion. #define NO_FILES 1 -/** - * Require a parameter after completion - */ +/// Require a parameter after completion. #define NO_COMMON 2 -/** - * Only use the argument list specifies with completion after - * option. This is the same as (NO_FILES | NO_COMMON) - */ +/// Only use the argument list specifies with completion after option. This is the same as (NO_FILES +/// | NO_COMMON). #define EXCLUSIVE 3 - -/** - * Command is a path - */ +/// Command is a path. #define PATH 1 -/** - * Command is not a path - */ +/// Command is not a path. #define COMMAND 0 - -/** - * Separator between completion and description - */ +/// Separator between completion and description. #define COMPLETE_SEP L'\004' - -/** - * Character that separates the completion and description on - * programmable completions - */ +/// Character that separates the completion and description on programmable completions. #define PROG_COMPLETE_SEP L'\t' -enum -{ - /** - Do not insert space afterwards if this is the only completion. (The - default is to try insert a space) - */ +enum { + /// Do not insert space afterwards if this is the only completion. (The default is to try insert + /// a space). COMPLETE_NO_SPACE = 1 << 0, - - /** This is not the suffix of a token, but replaces it entirely */ + /// This is not the suffix of a token, but replaces it entirely. COMPLETE_REPLACES_TOKEN = 1 << 2, - - /** This completion may or may not want a space at the end - guess by - checking the last character of the completion. */ + /// This completion may or may not want a space at the end - guess by checking the last + /// character of the completion. COMPLETE_AUTO_SPACE = 1 << 3, - - /** This completion should be inserted as-is, without escaping. */ + /// This completion should be inserted as-is, without escaping. COMPLETE_DONT_ESCAPE = 1 << 4, - - /** If you do escape, don't escape tildes */ + /// If you do escape, don't escape tildes. COMPLETE_DONT_ESCAPE_TILDES = 1 << 5 }; typedef int complete_flags_t; - -class completion_t -{ - -private: - /* No public default constructor */ +class completion_t { + private: + // No public default constructor. completion_t(); -public: - /* Destructor. Not inlining it saves code size. */ + public: + // Destructor. Not inlining it saves code size. ~completion_t(); - /** The completion string */ + /// The completion string. wcstring completion; - - /** The description for this completion */ + /// The description for this completion. wcstring description; - - /** The type of fuzzy match */ + /// The type of fuzzy match. string_fuzzy_match_t match; - - /** - Flags determining the completion behaviour. - - Determines whether a space should be inserted after this - completion 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. - */ + /// Flags determining the completion behaviour. + /// + /// Determines whether a space should be inserted after this completion 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. complete_flags_t flags; - /* Construction. Note: defining these so that they are not inlined reduces the executable size. */ - explicit completion_t(const wcstring &comp, const wcstring &desc = wcstring(), string_fuzzy_match_t match = string_fuzzy_match_t(fuzzy_match_exact), complete_flags_t flags_val = 0); + // Construction. Note: defining these so that they are not inlined reduces the executable size. + explicit completion_t(const wcstring &comp, const wcstring &desc = wcstring(), + string_fuzzy_match_t match = string_fuzzy_match_t(fuzzy_match_exact), + complete_flags_t flags_val = 0); completion_t(const completion_t &); completion_t &operator=(const completion_t &); - /* Compare two completions. No operating overlaoding to make this always explicit (there's potentially multiple ways to compare completions). */ - - /* "Naturally less than" means in a natural ordering, where digits are treated as numbers. For example, foo10 is naturally greater than foo2 (but alphabetically less than it) */ + // Compare two completions. No operating overlaoding to make this always explicit (there's + // potentially multiple ways to compare completions). + // + // "Naturally less than" means in a natural ordering, where digits are treated as numbers. For + // example, foo10 is naturally greater than foo2 (but alphabetically less than it). static bool is_naturally_less_than(const completion_t &a, const completion_t &b); static bool is_alphabetically_equal_to(const completion_t &a, const completion_t &b); - - /* If this completion replaces the entire token, prepend a prefix. Otherwise do nothing. */ + + // If this completion replaces the entire token, prepend a prefix. Otherwise do nothing. void prepend_token_prefix(const wcstring &prefix); }; -/** Sorts and remove any duplicate completions in the completion list, then puts them in priority order. */ +/// Sorts and remove any duplicate completions in the completion list, then puts them in priority +/// order. void completions_sort_and_prioritize(std::vector *comps); -enum -{ +enum { COMPLETION_REQUEST_DEFAULT = 0, - COMPLETION_REQUEST_AUTOSUGGESTION = 1 << 0, // indicates the completion is for an autosuggestion - COMPLETION_REQUEST_DESCRIPTIONS = 1 << 1, // indicates that we want descriptions - COMPLETION_REQUEST_FUZZY_MATCH = 1 << 2 // indicates that we don't require a prefix match + COMPLETION_REQUEST_AUTOSUGGESTION = 1 + << 0, // indicates the completion is for an autosuggestion + COMPLETION_REQUEST_DESCRIPTIONS = 1 << 1, // indicates that we want descriptions + COMPLETION_REQUEST_FUZZY_MATCH = 1 << 2 // indicates that we don't require a prefix match }; typedef uint32_t completion_request_flags_t; -/** - - Add a completion. - - All supplied values are copied, they should be freed by or otherwise - disposed by the caller. - - Examples: - - The command 'gcc -o' requires that a file follows it, so the - NO_COMMON option is suitable. This can be done using the following - line: - - complete -c gcc -s o -r - - The command 'grep -d' required that one of the strings 'read', - 'skip' or 'recurse' is used. As such, it is suitable to specify that - a completion requires one of them. This can be done using the - following line: - - complete -c grep -s d -x -a "read skip recurse" - - - \param cmd Command to complete. - \param cmd_type If cmd_type is PATH, cmd will be interpreted as the absolute - path of the program (optionally containing wildcards), otherwise it - will be interpreted as the command name. - \param short_opt The single character name of an option. (-a is a short option, - --all and -funroll are long options) - \param long_opt The multi character name of an option. (-a is a short option, - --all and -funroll are long options) - \param long_mode Whether to use old style, single dash long options. - \param result_mode Whether to search further completions when this - completion has been succesfully matched. If result_mode is SHARED, - any other completions may also be used. If result_mode is NO_FILES, - file completion should not be used, but other completions may be - used. If result_mode is NO_COMMON, on option may follow it - only a - parameter. If result_mode is EXCLUSIVE, no option may follow it, and - file completion is not performed. - \param comp A space separated list of completions which may contain subshells. - \param desc A description of the completion. - \param condition a command to be run to check it this completion should be used. - If \c condition is empty, the completion is always used. - \param flags A set of completion flags -*/ -enum complete_option_type_t -{ - option_type_args_only, // no option - option_type_short, // -x - option_type_single_long, // -foo - option_type_double_long // --foo +/// Add a completion. +/// +/// All supplied values are copied, they should be freed by or otherwise disposed by the caller. +/// +/// Examples: +/// +/// The command 'gcc -o' requires that a file follows it, so the NO_COMMON option is suitable. This +/// can be done using the following line: +/// +/// complete -c gcc -s o -r +/// +/// The command 'grep -d' required that one of the strings 'read', 'skip' or 'recurse' is used. As +/// such, it is suitable to specify that a completion requires one of them. This can be done using +/// the following line: +/// +/// complete -c grep -s d -x -a "read skip recurse" +/// +/// \param cmd Command to complete. +/// \param cmd_type If cmd_type is PATH, cmd will be interpreted as the absolute +/// path of the program (optionally containing wildcards), otherwise it +/// will be interpreted as the command name. +/// \param short_opt The single character name of an option. (-a is a short option, +/// --all and -funroll are long options) +/// \param long_opt The multi character name of an option. (-a is a short option, --all and +/// -funroll are long options) +/// \param long_mode Whether to use old style, single dash long options. +/// \param result_mode Whether to search further completions when this completion has been +/// succesfully matched. If result_mode is SHARED, any other completions may also be used. If +/// result_mode is NO_FILES, file completion should not be used, but other completions may be used. +/// If result_mode is NO_COMMON, on option may follow it - only a parameter. If result_mode is +/// EXCLUSIVE, no option may follow it, and file completion is not performed. +/// \param comp A space separated list of completions which may contain subshells. +/// \param desc A description of the completion. +/// \param condition a command to be run to check it this completion should be used. If \c condition +/// is empty, the completion is always used. +/// \param flags A set of completion flags +enum complete_option_type_t { + option_type_args_only, // no option + option_type_short, // -x + option_type_single_long, // -foo + option_type_double_long // --foo }; -void complete_add(const wchar_t *cmd, - bool cmd_is_path, - const wcstring &option, - complete_option_type_t option_type, - 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_add(const wchar_t *cmd, bool cmd_is_path, const wcstring &option, + complete_option_type_t option_type, 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); -/** - Remove a previously defined completion -*/ -void complete_remove(const wcstring &cmd, - bool cmd_is_path, - const wcstring &option, +/// Remove a previously defined completion. +void complete_remove(const wcstring &cmd, bool cmd_is_path, const wcstring &option, complete_option_type_t type); -/** Removes all completions for a given command */ +/// Removes all completions for a given command. void complete_remove_all(const wcstring &cmd, bool cmd_is_path); -/** Find all completions of the command cmd, insert them into out. - */ +/// Find all completions of the command cmd, insert them into out. class env_vars_snapshot_t; -void complete(const wcstring &cmd, - std::vector *out_comps, - completion_request_flags_t flags, - const env_vars_snapshot_t &vars); +void complete(const wcstring &cmd, std::vector *out_comps, + completion_request_flags_t flags, const env_vars_snapshot_t &vars); -/** - Return a list of all current completions. -*/ +/// Return a list of all current completions. wcstring complete_print(); -/** - 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); +/// 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); -/** - 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); +/// 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); +/// Create a new completion entry. +/// +/// \param completions The array of completions to append to +/// \param comp The completion string +/// \param desc The description of the completion +/// \param flags completion flags +void append_completion(std::vector *completions, const wcstring &comp, + const wcstring &desc = wcstring(), int flags = 0, + string_fuzzy_match_t match = string_fuzzy_match_t(fuzzy_match_exact)); -/** - Create a new completion entry - - \param completions The array of completions to append to - \param comp The completion string - \param desc The description of the completion - \param flags completion flags - -*/ -void append_completion(std::vector *completions, const wcstring &comp, const wcstring &desc = wcstring(), int flags = 0, string_fuzzy_match_t match = string_fuzzy_match_t(fuzzy_match_exact)); - -/* Function used for testing */ +/// Function used for testing. void complete_set_variable_names(const wcstring_list_t *names); -/* Support for "wrap targets." A wrap target is a command that completes liek another command. The target chain is the sequence of wraps (A wraps B wraps C...). Any loops in the chain are silently ignored. */ +/// Support for "wrap targets." A wrap target is a command that completes liek another command. The +/// target chain is the sequence of wraps (A wraps B wraps C...). Any loops in the chain are +/// silently ignored. bool complete_add_wrapper(const wcstring &command, const wcstring &wrap_target); bool complete_remove_wrapper(const wcstring &command, const wcstring &wrap_target); wcstring_list_t complete_get_wrap_chain(const wcstring &command); -/* Wonky interface: returns all wraps. Even-values are the commands, odd values are the targets. */ +// Wonky interface: returns all wraps. Even-values are the commands, odd values are the targets. wcstring_list_t complete_get_wrap_pairs(); #endif diff --git a/src/exec.cpp b/src/exec.cpp index b7735d62e..b55e7a26e 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -257,23 +257,16 @@ static bool io_transmogrify(const io_chain_t &in_chain, io_chain_t *out_chain, shared_ptr out; // gets allocated via new switch (in->io_mode) { - 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: { + // These redirections don't need transmogrification. They can be passed through. out = in; break; } - - // Transmogrify file redirections. case IO_FILE: { + // Transmogrify file redirections. int fd; CAST_INIT(io_file_t *, in_file, in.get()); if ((fd = open(in_file->filename_cstr, in_file->flags, OPEN_MASK)) == -1) { @@ -288,6 +281,12 @@ static bool io_transmogrify(const io_chain_t &in_chain, io_chain_t *out_chain, out.reset(new io_fd_t(in->fd, fd, false)); break; } + default: { + // Unknown type, should never happen. + fprintf(stderr, "Unknown io_mode %ld\n", (long)in->io_mode); + abort(); + break; + } } if (out.get() != NULL) result_chain.push_back(out); diff --git a/src/history.cpp b/src/history.cpp index dfb3608c1..f7f3affc6 100644 --- a/src/history.cpp +++ b/src/history.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -15,7 +16,6 @@ #include #include #include -#include #include "common.h" #include "env.h" @@ -473,9 +473,9 @@ static size_t offset_of_next_item(const char *begin, size_t mmap_length, offset_of_next_item_fish_1_x(begin, mmap_length, inout_cursor, cutoff_timestamp); break; } - default: - case history_type_unknown: { - // Oh well + case history_type_unknown: + default: { + // Oh well. result = (size_t)(-1); break; } @@ -804,12 +804,13 @@ done: 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: + case history_type_fish_1_x: { return history_t::decode_item_fish_1_x(base, len); - case history_type_fish_2_0: + } + case history_type_fish_2_0: { return history_t::decode_item_fish_2_0(base, len); - default: - return history_item_t(L""); + } + default: { return history_item_t(L""); } } } diff --git a/src/history.h b/src/history.h index a861f9784..0aa09b7c1 100644 --- a/src/history.h +++ b/src/history.h @@ -4,16 +4,16 @@ // IWYU pragma: no_include #include +#include #include #include #include #include +#include #include #include #include #include -#include -#include #include "common.h" #include "wutil.h" // IWYU pragma: keep diff --git a/src/input.cpp b/src/input.cpp index d37b963c0..0740a113e 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -256,10 +256,10 @@ void input_set_bind_mode(const wcstring &bm) { int input_function_arity(int function) { switch (function) { case R_FORWARD_JUMP: - case R_BACKWARD_JUMP: + case R_BACKWARD_JUMP: { return 1; - default: - return 0; + } + default: { return 0; } } } diff --git a/src/key_reader.cpp b/src/key_reader.cpp index bb4a0075b..881730771 100644 --- a/src/key_reader.cpp +++ b/src/key_reader.cpp @@ -5,32 +5,28 @@ Type ^C to exit the program. */ -#include -#include -#include -#include #include +#include +#include +#include #include +#include #include "common.h" #include "fallback.h" // IWYU pragma: keep #include "input_common.h" -int writestr(char *str) -{ +int writestr(char *str) { write_ignore(1, str, strlen(str)); return 0; } -int main(int argc, char **argv) -{ +int main(int argc, char **argv) { set_main_thread(); setup_fork_guards(); setlocale(LC_ALL, ""); - - if (argc == 2) - { + if (argc == 2) { static char term_buffer[2048]; char *termtype = getenv("TERM"); char *tbuff = new char[9999]; @@ -38,45 +34,35 @@ int main(int argc, char **argv) tgetent(term_buffer, termtype); res = tgetstr(argv[1], &tbuff); - if (res != 0) - { - while (*res != 0) - { + if (res != 0) { + while (*res != 0) { printf("%d ", *res); - res++; } printf("\n"); - } - else - { + } else { printf("Undefined sequence\n"); } - } - else - { + } else { 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); + 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 &= ~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; + 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: %u hex: %x char: %c\n", c, c, c); else @@ -84,7 +70,7 @@ int main(int argc, char **argv) writestr(scratch); } /* reset the terminal to the saved mode */ - tcsetattr(0,TCSANOW,&savemodes); + tcsetattr(0, TCSANOW, &savemodes); input_common_destroy(); } diff --git a/src/pager.cpp b/src/pager.cpp index 446e0704e..dff8b1a58 100644 --- a/src/pager.cpp +++ b/src/pager.cpp @@ -584,26 +584,27 @@ bool pager_t::select_next_completion_in_direction(selection_direction_t directio // Handle the case of nothing selected yet. if (selected_completion_idx == PAGER_SELECTION_NONE) { switch (direction) { - // These directions do something sane. case direction_south: case direction_page_south: case direction_next: - case direction_prev: + case direction_prev: { + // These directions do something sane. if (direction == direction_prev) { selected_completion_idx = completion_infos.size() - 1; } else { selected_completion_idx = 0; } return true; - - // These do nothing. + } case direction_north: case direction_page_north: case direction_east: case direction_west: case direction_deselect: - default: + default: { + // These do nothing. return false; + } } } @@ -651,7 +652,6 @@ bool pager_t::select_next_completion_in_direction(selection_direction_t directio } break; } - case direction_page_south: { if (current_row + page_height < rendering.rows) { current_row += page_height; @@ -675,7 +675,6 @@ bool pager_t::select_next_completion_in_direction(selection_direction_t directio } break; } - case direction_east: { // Go east, wrapping to the next row. There is no "row memory," so if we run off the // end, wrap. @@ -688,7 +687,6 @@ bool pager_t::select_next_completion_in_direction(selection_direction_t directio } break; } - case direction_west: { // Go west, wrapping to the previous row. if (current_col > 0) { @@ -699,10 +697,10 @@ bool pager_t::select_next_completion_in_direction(selection_direction_t directio } break; } - - default: + default: { assert(0 && "Unknown cardinal direction"); break; + } } // Compute the new index based on the changed row. diff --git a/src/parse_util.cpp b/src/parse_util.cpp index 85016244b..821669ed8 100644 --- a/src/parse_util.cpp +++ b/src/parse_util.cpp @@ -540,13 +540,15 @@ wcstring parse_util_escape_string_with_quote(const wcstring &cmd, wchar_t quote) case L'\n': case L'\t': case L'\b': - case L'\r': + case L'\r': { unescapable = true; break; - default: + } + default: { if (c == quote) result.push_back(L'\\'); result.push_back(c); break; + } } } @@ -815,21 +817,25 @@ static wcstring truncate_string(const wcstring &str) { /// For example, if wc is @, then the variable name was $@ and we suggest $argv. static const wchar_t *error_format_for_character(wchar_t wc) { switch (wc) { - case L'?': + case L'?': { return ERROR_NOT_STATUS; - case L'#': + } + case L'#': { return ERROR_NOT_ARGV_COUNT; - case L'@': + } + case L'@': { return ERROR_NOT_ARGV_AT; - case L'*': + } + case L'*': { return ERROR_NOT_ARGV_STAR; + } case L'$': case VARIABLE_EXPAND: case VARIABLE_EXPAND_SINGLE: - case VARIABLE_EXPAND_EMPTY: + case VARIABLE_EXPAND_EMPTY: { return ERROR_NOT_PID; - default: - return ERROR_BAD_VAR_CHAR1; + } + default: { return ERROR_BAD_VAR_CHAR1; } } } @@ -1156,7 +1162,6 @@ parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, BACKGROUND_IN_CONDITIONAL_ERROR_MSG); break; } - case symbol_job_list: { // This isn't very complete, e.g. we don't catch 'foo & ; not and bar'. assert(node_tree.get_child(*job_parent, 0) == &node); @@ -1195,9 +1200,7 @@ parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, } break; } - - default: - break; + default: { break; } } } } else if (node.type == symbol_plain_statement) { @@ -1268,23 +1271,24 @@ parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, if (loop_or_function_header != NULL) { switch (loop_or_function_header->type) { case symbol_while_header: - case symbol_for_header: + case symbol_for_header: { // This is a loop header, so we can break or continue. found_loop = true; end_search = true; break; - - case symbol_function_header: + } + case symbol_function_header: { // This is a function header, so we cannot break or // continue. We stop our search here. found_loop = false; end_search = true; break; - - default: + } + default: { // Most likely begin / end style block, which makes no // difference. break; + } } } ancestor = node_tree.get_parent(*ancestor); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 37263fe23..746807eb7 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -686,8 +686,7 @@ bool move_word_state_machine_t::consume_char_path_components(wchar_t c) { case s_separator: { if (!iswspace(c) && !is_path_component_character(c)) { consumed = true; // consumed separator - } - else { + } else { state = s_end; } break; diff --git a/src/wgetopt.cpp b/src/wgetopt.cpp index d85b5e748..41b7896a1 100644 --- a/src/wgetopt.cpp +++ b/src/wgetopt.cpp @@ -45,7 +45,7 @@ // Don't include stdlib.h for non-GNU C libraries because some of them contain conflicting // prototypes for getopt. #include -#endif // GNU C library. +#endif // GNU C library. // This version of `getopt' appears to the caller like standard Unix `getopt' but it behaves // differently for the user, since it allows the user to intersperse the options with the other @@ -105,10 +105,10 @@ static wchar_t *my_index(const wchar_t *str, int chr) { // gcc with -traditional declares the built-in strlen to return int, and has done so at least since // version 2.4.5. -- rms. extern int wcslen(const wchar_t *); -#endif // not __STDC__ -#endif // __GNUC__ +#endif // not __STDC__ +#endif // __GNUC__ -#endif // not __GNU_LIBRARY__ +#endif // not __GNU_LIBRARY__ // Exchange two adjacent subsequences of ARGV. One subsequence is elements // [first_nonopt,last_nonopt) which contains all the non-options that have been skipped so far. The @@ -303,10 +303,12 @@ int wgetopter_t::_wgetopt_internal(int argc, wchar_t **argv, const wchar_t *opts const struct woption *pfound = NULL; int exact = 0; int ambig = 0; - int indfound = 0; // set to zero by Anton + int indfound = 0; // set to zero by Anton int option_index; - for (nameend = nextchar; *nameend && *nameend != '='; nameend++) /* Do nothing. */; + 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++) @@ -344,7 +346,7 @@ int wgetopter_t::_wgetopt_internal(int argc, wchar_t **argv, const wchar_t *opts woptarg = nameend + 1; else { if (wopterr) { - if (argv[woptind - 1][1] == '-') // --option + if (argv[woptind - 1][1] == '-') // --option fwprintf(stderr, _(L"%ls: Option '--%ls' doesn't allow an argument\n"), argv[0], pfound->name); else @@ -380,7 +382,7 @@ int wgetopter_t::_wgetopt_internal(int argc, wchar_t **argv, const wchar_t *opts // short option. if (!long_only || argv[woptind][1] == '-' || my_index(optstring, *nextchar) == NULL) { if (wopterr) { - if (argv[woptind][1] == '-') // --option + if (argv[woptind][1] == '-') // --option fwprintf(stderr, _(L"%ls: Unrecognized option '--%ls'\n"), argv[0], nextchar); else // +option or -option diff --git a/src/wgetopt.h b/src/wgetopt.h index 95e1462d9..5cf60ea0c 100644 --- a/src/wgetopt.h +++ b/src/wgetopt.h @@ -68,7 +68,7 @@ class wgetopter_t { int woptind; // The next char to be scanned in the option-element in which the last option character we - // returned was found. This allows us to pick up the scan where we left off. + // returned was found. This allows us to pick up the scan where we left off. // // If this is zero, or a null string, it means resume the scan by advancing to the next // ARGV-element. diff --git a/src/wildcard.cpp b/src/wildcard.cpp index 1a09d63e9..93242570e 100644 --- a/src/wildcard.cpp +++ b/src/wildcard.cpp @@ -94,7 +94,7 @@ static enum fuzzy_match_type_t wildcard_match_internal(const wchar_t *str, const bool leading_dots_fail_to_match, bool is_first) { if (*str == 0 && *wc == 0) { - return fuzzy_match_exact; // we're done + return fuzzy_match_exact; // we're done } // Hackish fix for issue #270. Prevent wildcards from matching . or .., but we must still allow @@ -261,7 +261,6 @@ static bool wildcard_complete_internal(const wchar_t *str, const wchar_t *wc, } break; } - case ANY_STRING: { // Hackish. If this is the last character of the wildcard, then just complete with // the empty string. This fixes cases like "f*" -> "f*o". @@ -289,14 +288,14 @@ static bool wildcard_complete_internal(const wchar_t *str, const wchar_t *wc, } return has_match; } - - case ANY_STRING_RECURSIVE: + case ANY_STRING_RECURSIVE: { // We don't even try with this one. return false; - - default: + } + default: { assert(0 && "Unreachable code reached"); return false; + } } } assert(0 && "Unreachable code reached"); @@ -535,9 +534,9 @@ class wildcard_expander_t { wcstring child_entry; while (wreaddir_resolving(dir, abs_unique_hierarchy, child_entry, &child_is_dir)) { if (child_entry.empty() || child_entry.at(0) == L'.') { - continue; // either hidden, or . and .. entries -- skip them + continue; // either hidden, or . and .. entries -- skip them } else if (child_is_dir && unique_entry.empty()) { - unique_entry = child_entry; // first candidate + unique_entry = child_entry; // first candidate } else { // We either have two or more candidates, or the child is not a directory. We're // done. From 3ad5c7c2890dca8e75bd116c17d7d9fe5c9ec608 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Tue, 3 May 2016 19:15:27 -0700 Subject: [PATCH 215/363] add missing fallback declarations Fixes #2993 --- src/fallback.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/fallback.h b/src/fallback.h index ca7a671b5..59eba2242 100644 --- a/src/fallback.h +++ b/src/fallback.h @@ -270,4 +270,12 @@ long sysconf(int name); double nan(char *tagp); #endif +#ifndef HAVE_BACKTRACE +int backtrace(void **buffer, int size); +#endif + +#ifndef HAVE_BACKTRACE_SYMBOLS_FD +char **backtrace_symbols_fd(void *const *buffer, int size, int fd); +#endif + #endif From d97c22df2daaeca6c2cec7f513babb667618f1f0 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Mon, 2 May 2016 20:53:48 -0700 Subject: [PATCH 216/363] add floating point output to `math` command This makes it easy for the user to request floating point output with the desired number of digits after the decimal point (not to be confused with significant digits). Note that this is just a thin wrapper so someone can say `math -s3 10 / 3` rather than `math "scale=3; 10 /3"`. Resolves #1643 --- doc_src/math.txt | 20 ++++++++++++-- share/functions/math.fish | 57 ++++++++++++++++++++++++++------------- tests/math.err | 0 tests/math.in | 9 +++++++ tests/math.out | 8 ++++++ tests/math.status | 1 + 6 files changed, 74 insertions(+), 21 deletions(-) create mode 100644 tests/math.err create mode 100644 tests/math.in create mode 100644 tests/math.out create mode 100644 tests/math.status diff --git a/doc_src/math.txt b/doc_src/math.txt index 994f15152..a22b0e6ea 100644 --- a/doc_src/math.txt +++ b/doc_src/math.txt @@ -1,9 +1,8 @@ - \section math math - Perform mathematics calculations \subsection math-synopsis Synopsis \fish{synopsis} -math EXPRESSION +math [-sN] EXPRESSION \endfish \subsection math-description Description @@ -12,9 +11,26 @@ math EXPRESSION For a description of the syntax supported by math, see the manual for the bc program. Keep in mind that parameter expansion takes place on any expressions before they are evaluated. This can be very useful in order to perform calculations involving shell variables or the output of command substitutions, but it also means that parenthesis have to be escaped. +The following options are available: + +- `-sN` Sets the scale of the result. `N` must be an integer and defaults to zero. This simply sets bc's `scale` variable to the provided value. Note that you cannot put a space between `-s` and `N`. + +\subsection return-values Return Values + +If invalid options or no expression is provided the return `status` is two. If the expression is invalid the return `status` is three. If bc returns a result of `0` (literally, not `0.0` or similar variants) the return `status` is one otherwise it's zero. \subsection math-example Examples `math 1+1` outputs 2. `math $status-128` outputs the numerical exit status of the last command minus 128. + +`math 10 / 6` outputs `1`. + +`math -s0 10.0 / 6.0` outputs `1`. + +`math -s3 10 / 6` outputs `1.666`. + +\subsection math-cautions Cautions + +Note that the modulo operator (`x % y`) is not well defined for floating point arithmetic. The `bc` command produces a nonsensical result rather than emit an error and fail in that case. It doesn't matter if the arguments are integers; e.g., `10 % 4`. You'll still get an incorrect result. Do not use the `-sN` flag with N greater than zero if you want sensible answers when using the modulo operator. diff --git a/share/functions/math.fish b/share/functions/math.fish index c190ec631..ed638c695 100644 --- a/share/functions/math.fish +++ b/share/functions/math.fish @@ -1,23 +1,42 @@ - function math --description "Perform math calculations in bc" - if count $argv >/dev/null - switch $argv[1] - case -h --h --he --hel --help - __fish_print_help math - return 0 - end + set -l scale 0 # default is integer arithmetic - # Stitch lines together manually - # we can't rely on BC_LINE_LENGTH because some systems don't have a bc version "new" enough - set -l out (echo $argv | bc | string replace -r '\\\\$' '' | string join '') - test -z "$out"; and return 1 - echo $out - switch $out - case 0 - return 1 - end - return 0 - end - return 2 + if set -q argv[1] + switch $argv[1] + case '-s*' # user wants to specify the scale of the output + set scale (string replace -- '-s' '' $argv[1]) + if not string match -q -r '^\d+$' "$scale" + echo 'Expected an integer to follow -s' >&2 + return 2 # missing argument is an error + end + set -e argv[1] + case -h --h --he --hel --help + __fish_print_help math + return 0 + end + end + + if not set -q argv[1] + return 2 # no arguments is an error + end + + # Stitch lines together manually. We can't rely on BC_LINE_LENGTH because some systems don't + # have a new enough version of bc. + set -l out (echo "scale=$scale; $argv" | bc | string replace -r '\\\\$' '' | string join '') + switch "$out" + case '' + # No output indicates an error occurred. + return 3 + + case 0 + # For historical reasons a zero result translates to a failure status. + echo 0 + return 1 + + case '*' + # For historical reasons a non-zero result translates to a success status. + echo $out + return 0 + end end diff --git a/tests/math.err b/tests/math.err new file mode 100644 index 000000000..e69de29bb diff --git a/tests/math.in b/tests/math.in new file mode 100644 index 000000000..d8cb50922 --- /dev/null +++ b/tests/math.in @@ -0,0 +1,9 @@ +math 3 / 2 +math 10/6 +math -s0 10 / 6 +math -s3 10/6 +math '10 % 6' +math -s0 '10 % 6' +math '23 % 7' +math -s6 '5 / 3 * 0.3' +true diff --git a/tests/math.out b/tests/math.out new file mode 100644 index 000000000..0a46b6a56 --- /dev/null +++ b/tests/math.out @@ -0,0 +1,8 @@ +1 +1 +1 +1.666 +4 +4 +2 +.499999 diff --git a/tests/math.status b/tests/math.status new file mode 100644 index 000000000..573541ac9 --- /dev/null +++ b/tests/math.status @@ -0,0 +1 @@ +0 From e1a706bd7770e17e36de61eaf1597d39288e7f0d Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Tue, 3 May 2016 19:37:27 -0700 Subject: [PATCH 217/363] limit `make style-all` to fish scripts in share I noticed that if I've previous done `make test` that a subsequent `make style-all` attempts to restyle all the fish scripts in the *test* directory. Those files are transient and not part of the git repository. Limit restyling all fish scripts just to those in the *share* directory tree. There are a couple elsewhere in the repo (e.g., *build_tools*) but they can be handled on an individual basis. --- build_tools/style.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_tools/style.fish b/build_tools/style.fish index fc9a96e3a..ee54216b4 100755 --- a/build_tools/style.fish +++ b/build_tools/style.fish @@ -30,7 +30,7 @@ if test $all = yes exit 1 end set c_files src/*.h src/*.cpp - set f_files ***.fish + set f_files share/***.fish else # We haven't been asked to reformat all the source. If there are uncommitted changes reformat # those using `git clang-format`. Else reformat the files in the most recent commit. From 42068931c734437d6da19037721b04cf0d125c1c Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Tue, 3 May 2016 21:31:32 -0700 Subject: [PATCH 218/363] eliminate "useless parentheses" lint errors Some `oclint` errors regarding "useless parentheses" are meaningfull. But the vast majority are bogus in as much as removing the parentheses reduces readability. So fix a few of the egregious uses and otherwise suppress that error. --- .oclint | 15 +++++++++++++-- src/builtin.cpp | 2 +- src/color.cpp | 6 +++--- src/parse_util.cpp | 6 +++--- src/screen.cpp | 14 +++++++------- src/utf8.cpp | 31 +++++++++++++++---------------- 6 files changed, 42 insertions(+), 32 deletions(-) diff --git a/.oclint b/.oclint index cd68e1821..4e9870e6e 100644 --- a/.oclint +++ b/.oclint @@ -2,7 +2,18 @@ rules: rule-configurations: # This is the default value (as of the time I wrote this) but I'm making # it explicit since it needs to agree with the value used by clang-format. - # Thus, if we ever change the fish style to allow longer lines this should - # be changed (as well as the corresponding clang-format config). + # Thus, if we ever change the fish style to allow longer or shorter lines + # this should be changed (as well as the corresponding .clang-format file). - key: LONG_LINE value: 100 + +disable-rules: + # A few instances of "useless parentheses" errors are meaningful. Mostly + # in the context of the `return` statement. Unfortunately the vast + # majority would result in removing parentheses that decreases + # readability. So we're going to ignore this warning and rely on humans to + # notice when the parentheses are truly not needed. + # + # Also, some macro expansions, such as FD_SET(), trigger this warning and + # we don't want to suppress each of those individually. + - UselessParentheses diff --git a/src/builtin.cpp b/src/builtin.cpp index 8ed2858ba..615df6af2 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -163,7 +163,7 @@ wcstring builtin_help_get(parser_t &parser, io_streams_t &streams, const wchar_t /// to an interactive screen, it may be shortened to fit the screen. void builtin_print_help(parser_t &parser, io_streams_t &streams, const wchar_t *cmd, output_stream_t &b) { - bool is_stderr = (&b == &streams.err); + bool is_stderr = &b == &streams.err; if (is_stderr) { b.append(parser.current_line()); } diff --git a/src/color.cpp b/src/color.cpp index 6b920d15d..7ab96779b 100644 --- a/src/color.cpp +++ b/src/color.cpp @@ -89,8 +89,8 @@ static unsigned long squared_difference(long p1, long p2) { 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); + unsigned long best_distance = (unsigned long)-1; + unsigned char best_index = (unsigned char)-1; 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, @@ -288,7 +288,7 @@ unsigned char rgb_color_t::to_name_index() const { } else if (type == type_rgb) { return term8_color_for_rgb(data.color.rgb); } else { - return (unsigned char)(-1); // this is an error + return (unsigned char)-1; // this is an error } } diff --git a/src/parse_util.cpp b/src/parse_util.cpp index 821669ed8..f3868435e 100644 --- a/src/parse_util.cpp +++ b/src/parse_util.cpp @@ -595,7 +595,7 @@ static void compute_indents_recursive(const parse_node_tree_t &tree, node_offset const bool is_root_job_list = node_type != parent_type && (node_type == symbol_job_list || node_type == symbol_andor_job_list); const bool is_root_case_item_list = - (node_type == symbol_case_item_list && parent_type != symbol_case_item_list); + node_type == symbol_case_item_list && parent_type != symbol_case_item_list; if (is_root_job_list || is_root_case_item_list) { node_indent += 1; } @@ -845,7 +845,7 @@ void parse_util_expand_variable_error(const wcstring &token, size_t global_token // dollar sign. assert(errors != NULL); assert(dollar_pos < token.size()); - const bool double_quotes = (token.at(dollar_pos) == VARIABLE_EXPAND_SINGLE); + const bool double_quotes = token.at(dollar_pos) == VARIABLE_EXPAND_SINGLE; const size_t start_error_count = errors->size(); const size_t global_dollar_pos = global_token_pos + dollar_pos; const size_t global_after_dollar_pos = global_dollar_pos + 1; @@ -1043,7 +1043,7 @@ parser_test_error_bits_t parse_util_detect_errors_in_argument(const parse_node_t switch (unesc.at(idx)) { case VARIABLE_EXPAND: case VARIABLE_EXPAND_SINGLE: { - wchar_t next_char = (idx + 1 < unesc_size ? unesc.at(idx + 1) : L'\0'); + wchar_t next_char = idx + 1 < unesc_size ? unesc.at(idx + 1) : L'\0'; if (next_char != VARIABLE_EXPAND && next_char != VARIABLE_EXPAND_SINGLE && !wcsvarchr(next_char)) { diff --git a/src/screen.cpp b/src/screen.cpp index 197469424..d863448a7 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -89,7 +89,7 @@ static size_t try_sequence(const char *seq, const wchar_t *str) { /// 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) { // Assume tab stops every 8 characters if undefined. - size_t tab_width = (init_tabs > 0 ? (size_t)init_tabs : 8); + size_t tab_width = init_tabs > 0 ? (size_t)init_tabs : 8; return ((in / tab_width) + 1) * tab_width; } @@ -496,8 +496,8 @@ static void s_move(screen_t *s, data_buffer_t *b, int new_x, int new_y) { // Use the bulk ('multi') output for cursor movement if it is supported and it would be shorter // Note that this is required to avoid some visual glitches in iTerm (issue #1448). - bool use_multi = (multi_str != NULL && multi_str[0] != '\0' && - abs(x_steps) * strlen(str) > strlen(multi_str)); + bool use_multi = multi_str != NULL && multi_str[0] != '\0' && + abs(x_steps) * strlen(str) > strlen(multi_str); if (use_multi) { char *multi_param = tparm(multi_str, abs(x_steps)); writembs(multi_param); @@ -699,12 +699,12 @@ static void s_update(screen_t *scr, const wchar_t *left_prompt, const wchar_t *r 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); + size_t start_pos = i == 0 ? left_prompt_width : 0; int current_width = 0; // If this is the last line, maybe we should clear the screen. const bool should_clear_screen_this_line = - (need_clear_screen && i + 1 == scr->desired.line_count() && clr_eos != NULL); + need_clear_screen && i + 1 == scr->desired.line_count() && clr_eos != NULL; // Note that skip_remaining is a width, not a character count. size_t skip_remaining = start_pos; @@ -788,7 +788,7 @@ static void s_update(screen_t *scr, const wchar_t *left_prompt, const wchar_t *r clear_remainder = true; } else { int prev_width = - (s_line.text.empty() ? 0 : fish_wcswidth(&s_line.text.at(0), s_line.text.size())); + s_line.text.empty() ? 0 : fish_wcswidth(&s_line.text.at(0), s_line.text.size()); clear_remainder = prev_width > current_width; } if (clear_remainder) { @@ -842,7 +842,7 @@ static void s_update(screen_t *scr, const wchar_t *left_prompt, const wchar_t *r } /// Returns true if we are using a dumb terminal. -static bool is_dumb(void) { return (!cursor_up || !cursor_down || !cursor_left || !cursor_right); } +static bool is_dumb(void) { return !cursor_up || !cursor_down || !cursor_left || !cursor_right; } struct screen_layout_t { // The left prompt that we're going to use. diff --git a/src/utf8.cpp b/src/utf8.cpp index ceec637e9..9c8bcf6d2 100644 --- a/src/utf8.cpp +++ b/src/utf8.cpp @@ -127,9 +127,8 @@ static int __utf8_forbitten(unsigned char octet); static int __wchar_forbitten(utf8_wchar_t sym) { // Surrogate pairs. - if (sym >= 0xd800 && sym <= 0xdfff) return (-1); - - return (0); + if (sym >= 0xd800 && sym <= 0xdfff) return -1; + return 0; } static int __utf8_forbitten(unsigned char octet) { @@ -138,11 +137,11 @@ static int __utf8_forbitten(unsigned char octet) { case 0xc1: case 0xf5: case 0xff: { - return (-1); + return -1; } } - return (0); + return 0; } /// This function translates UTF-8 string into UCS-2 or UCS-4 string (all symbols will be in local @@ -170,7 +169,7 @@ static size_t utf8_to_wchar_internal(const char *in, size_t insize, utf8_wstring utf8_wchar_t high; size_t n, total, i, n_bits; - if (in == NULL || insize == 0) return (0); + if (in == NULL || insize == 0) return 0; if (out_string != NULL) out_string->clear(); @@ -179,7 +178,7 @@ static size_t utf8_to_wchar_internal(const char *in, size_t insize, utf8_wstring lim = p + insize; for (; p < lim; p += n) { - if (__utf8_forbitten(*p) != 0 && (flags & UTF8_IGNORE_ERROR) == 0) return (0); + if (__utf8_forbitten(*p) != 0 && (flags & UTF8_IGNORE_ERROR) == 0) return 0; // Get number of bytes for one wide character. n = 1; // default: 1 byte. Used when skipping bytes @@ -201,13 +200,13 @@ static size_t utf8_to_wchar_internal(const char *in, size_t insize, utf8_wstring n = 6; high = (utf8_wchar_t)(*p & 0x01); } else { - if ((flags & UTF8_IGNORE_ERROR) == 0) return (0); + if ((flags & UTF8_IGNORE_ERROR) == 0) return 0; continue; } // Does the sequence header tell us truth about length? if (lim - p <= n - 1) { - if ((flags & UTF8_IGNORE_ERROR) == 0) return (0); + if ((flags & UTF8_IGNORE_ERROR) == 0) return 0; n = 1; continue; // skip } @@ -218,7 +217,7 @@ static size_t utf8_to_wchar_internal(const char *in, size_t insize, utf8_wstring if ((p[i] & 0xc0) != _NXT) break; } if (i != n) { - if ((flags & UTF8_IGNORE_ERROR) == 0) return (0); + if ((flags & UTF8_IGNORE_ERROR) == 0) return 0; n = 1; continue; // skip } @@ -256,7 +255,7 @@ static size_t utf8_to_wchar_internal(const char *in, size_t insize, utf8_wstring } } - return (total); + return total; } /// This function translates UCS-2/4 symbols (given in local machine byte order) into UTF-8 string. @@ -278,7 +277,7 @@ static size_t wchar_to_utf8_internal(const utf8_wchar_t *in, size_t insize, char unsigned char *p, *lim; size_t total, n; - if (in == NULL || insize == 0 || (outsize == 0 && out != NULL)) return (0); + if (in == NULL || insize == 0 || (outsize == 0 && out != NULL)) return 0; w = in; wlim = w + insize; @@ -288,7 +287,7 @@ static size_t wchar_to_utf8_internal(const utf8_wchar_t *in, size_t insize, char for (; w < wlim; w++) { if (__wchar_forbitten(*w) != 0) { if ((flags & UTF8_IGNORE_ERROR) == 0) - return (0); + return 0; else continue; } @@ -297,7 +296,7 @@ static size_t wchar_to_utf8_internal(const utf8_wchar_t *in, size_t insize, char const int32_t w_wide = *w; if (w_wide < 0) { - if ((flags & UTF8_IGNORE_ERROR) == 0) return (0); + if ((flags & UTF8_IGNORE_ERROR) == 0) return 0; continue; } else if (w_wide <= 0x0000007f) n = 1; @@ -316,7 +315,7 @@ static size_t wchar_to_utf8_internal(const utf8_wchar_t *in, size_t insize, char if (out == NULL) continue; - if (lim - p <= n - 1) return (0); /* no space left */ + if (lim - p <= n - 1) return 0; // no space left // Extract the wchar_t as big-endian. If wchar_t is UCS-16, the first two bytes will be 0. unsigned char oc[4]; @@ -376,5 +375,5 @@ static size_t wchar_to_utf8_internal(const utf8_wchar_t *in, size_t insize, char p += n; } - return (total); + return total; } From 527e5f52ba5a097a24490065fea23b4627032e4c Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 4 May 2016 14:51:09 +0200 Subject: [PATCH 219/363] Remove useless case completions It doesn't take options and what it takes (arbitrary strings) we can't sensibly complete. --- share/completions/case.fish | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 share/completions/case.fish diff --git a/share/completions/case.fish b/share/completions/case.fish deleted file mode 100644 index 009931da1..000000000 --- a/share/completions/case.fish +++ /dev/null @@ -1,3 +0,0 @@ - -complete -c case -s h -l help --description 'Display help and exit' -complete -c case -u From 862e0127f63ddd4775ed18ce025a7d9216384b83 Mon Sep 17 00:00:00 2001 From: David Adam Date: Thu, 5 May 2016 06:01:14 +0800 Subject: [PATCH 220/363] CHANGELOG: update for 2.3b2 --- CHANGELOG.md | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26280dc8c..1d7e72724 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +# fish 2.3b2 (released May 5, 2016) + +## Significant changes + +- A new `fish_realpath` builtin and associated function to allow the use of `realpath` even on those platforms that don't ship an appropriate command (#2932). +- Alt-# toggles the current command line between commented and uncommented states, making it easy to save a command in history without executing it. +- The `fish_vi_mode` function is now deprecated in favour of `fish_vi_key_bindings`. + +## Other notable fixes and improvements + +- Fix the build on Cygwin (#2952) and RedHat Enterprise Linux/CentOS 5 (#2955). +- Avoid confusing the terminal line driver with non-printing characters in `fish_title` (#2453). +- Improved completions for busctl, git (#2585, #2879, #2984), and netctl. + # fish 2.3b1 (released April 19, 2016) ## Significant Changes @@ -7,7 +21,7 @@ - Add new directories for vendor functions and configuration snippets (#2500) - A new `fish_realpath` builtin and associated `realpath` function should allow scripts to resolve path names via `realpath` regardless of whether there is an external command of that name; albeit with some limitations. See the associated documentation. -# Backward-incompatible changes +## Backward-incompatible changes - Unmatched globs will now cause an error, except when used with `for`, `set` or `count` (#2719) - `and` and `or` will now bind to the closest `if` or `while`, allowing compound conditions without `begin` and `end` (#1428) @@ -16,7 +30,7 @@ - `source` no longer puts the file name in `$argv` if no arguments are given (#139) - History files are stored under the `XDG_DATA_HOME` hierarchy (by default, in `~/.local/share`), and existing history will be moved on first use (#744) -# Other notable fixes and improvements +## Other notable fixes and improvements - Fish no longer silences errors in config.fish (#2702) - Directory autosuggestions will now descend as far as possible if there is only one child directory (#2531) From 49838c768d850ebb530f5f048165cb51f6178d63 Mon Sep 17 00:00:00 2001 From: David Adam Date: Thu, 5 May 2016 06:01:39 +0800 Subject: [PATCH 221/363] bump version for 2.3b2 --- osx/Info.plist | 2 +- osx/config.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osx/Info.plist b/osx/Info.plist index 899ed3d58..d977fe4da 100644 --- a/osx/Info.plist +++ b/osx/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 2.2.990 + 2.2.991 CFBundleVersion 0.1 LSApplicationCategoryType diff --git a/osx/config.h b/osx/config.h index 202567aae..a2b4e7a61 100644 --- a/osx/config.h +++ b/osx/config.h @@ -222,7 +222,7 @@ #define PACKAGE_NAME "fish" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "fish 2.3b1" +#define PACKAGE_STRING "fish 2.3b2" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "fish" @@ -231,7 +231,7 @@ #define PACKAGE_URL "" /* Define to the version of this package. */ -#define PACKAGE_VERSION "2.3b1" +#define PACKAGE_VERSION "2.3b2" /* The size of `wchar_t', as computed by sizeof. */ #define SIZEOF_WCHAR_T 4 From 79f342b954a6f47ee3f1277a899066a654e7c330 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Wed, 4 May 2016 15:19:47 -0700 Subject: [PATCH 222/363] lint cleanup: eliminate "redundant" errors This removes some pointless parentheses but the primary focus is removing redundancies like unnecessary "else" clauses. --- src/builtin.cpp | 63 +++++++-------- src/builtin_commandline.cpp | 3 +- src/builtin_string.cpp | 20 +++-- src/builtin_test.cpp | 40 +++++----- src/color.cpp | 10 +-- src/common.cpp | 23 ++---- src/complete.cpp | 17 ++-- src/env.cpp | 11 +-- src/env_universal_common.cpp | 13 +-- src/event.cpp | 3 +- src/exec.cpp | 5 +- src/expand.cpp | 123 +++++++++++++++-------------- src/fallback.cpp | 22 +++--- src/fish_tests.cpp | 2 +- src/function.cpp | 3 +- src/highlight.cpp | 3 +- src/history.cpp | 14 ++-- src/input.cpp | 23 +++--- src/kill.cpp | 8 +- src/output.cpp | 9 +-- src/parse_execution.cpp | 19 ++--- src/parse_tree.cpp | 6 +- src/parse_util.cpp | 51 ++++++------ src/parser.cpp | 3 +- src/path.cpp | 137 ++++++++++++++++---------------- src/proc.cpp | 19 +++-- src/reader.cpp | 107 +++++++++++++------------ src/reader.h | 2 +- src/tokenizer.cpp | 20 ++--- src/utf8.cpp | 28 +++---- src/util.cpp | 2 +- src/wcstringutil.cpp | 6 +- src/wildcard.cpp | 148 +++++++++++++++++------------------ src/wutil.cpp | 24 +++--- 34 files changed, 456 insertions(+), 531 deletions(-) diff --git a/src/builtin.cpp b/src/builtin.cpp index 615df6af2..db528aa0e 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -412,26 +412,25 @@ static int builtin_bind_erase(wchar_t **seq, int all, const wchar_t *mode, int u } return 0; - } else { - int res = 0; - - if (mode == NULL) mode = DEFAULT_BIND_MODE; - - while (*seq) { - if (use_terminfo) { - wcstring seq2; - if (get_terminfo_sequence(*seq++, &seq2, streams)) { - input_mapping_erase(seq2, mode); - } else { - res = 1; - } - } else { - input_mapping_erase(*seq++, mode); - } - } - - return res; } + + int res = 0; + if (mode == NULL) mode = DEFAULT_BIND_MODE; + + while (*seq) { + if (use_terminfo) { + wcstring seq2; + if (get_terminfo_sequence(*seq++, &seq2, streams)) { + input_mapping_erase(seq2, mode); + } else { + res = 1; + } + } else { + input_mapping_erase(*seq++, mode); + } + } + + return res; } /// The bind builtin, used for setting character sequences. @@ -1454,11 +1453,10 @@ static int builtin_pwd(parser_t &parser, io_streams_t &streams, wchar_t **argv) wcstring res = wgetcwd(); if (res.empty()) { return STATUS_BUILTIN_ERROR; - } else { - streams.out.append(res); - streams.out.push_back(L'\n'); - return STATUS_BUILTIN_OK; } + streams.out.append(res); + streams.out.push_back(L'\n'); + return STATUS_BUILTIN_OK; } /// Adds a function to the function set. It calls into function.cpp to perform any heavy lifting. @@ -2638,10 +2636,10 @@ static int send_to_bg(parser_t &parser, io_streams_t &streams, job_t *j, const w L"bg", j->job_id, j->command_wcstr()); builtin_print_help(parser, streams, L"bg", streams.err); return STATUS_BUILTIN_ERROR; - } else { - streams.err.append_format(_(L"Send job %d '%ls' to background\n"), j->job_id, - j->command_wcstr()); } + + streams.err.append_format(_(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)); @@ -3087,9 +3085,8 @@ static const builtin_data_t *builtin_lookup(const wcstring &name) { const builtin_data_t *found = std::lower_bound(builtin_datas, array_end, name); if (found != array_end && name == found->name) { return found; - } else { - return NULL; } + return NULL; } void builtin_init() { @@ -3127,14 +3124,10 @@ int builtin_run(parser_t &parser, const wchar_t *const *argv, io_streams_t &stre } if (data != NULL) { - int status; - - status = cmd(parser, streams, argv); - return status; - - } else { - debug(0, UNKNOWN_BUILTIN_ERR_MSG, argv[0]); + return cmd(parser, streams, argv); } + + debug(0, UNKNOWN_BUILTIN_ERR_MSG, argv[0]); return STATUS_BUILTIN_ERROR; } diff --git a/src/builtin_commandline.cpp b/src/builtin_commandline.cpp index 0ccea6165..c7b82d7af 100644 --- a/src/builtin_commandline.cpp +++ b/src/builtin_commandline.cpp @@ -436,11 +436,10 @@ int builtin_commandline(parser_t &parser, io_streams_t &streams, wchar_t **argv) 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 { streams.out.append_format(L"%lu\n", (unsigned long)reader_get_cursor_pos()); - return 0; } + return 0; } if (line_mode) { diff --git a/src/builtin_string.cpp b/src/builtin_string.cpp index 9e6c11a42..edb6320fe 100644 --- a/src/builtin_string.cpp +++ b/src/builtin_string.cpp @@ -67,9 +67,8 @@ static const wchar_t *string_get_arg_stdin(wcstring *storage, const io_streams_t if (rc == 0) { // EOF if (arg.empty()) { return 0; - } else { - break; } + break; } if (ch == '\n') { @@ -84,16 +83,15 @@ static const wchar_t *string_get_arg_stdin(wcstring *storage, const io_streams_t } static const wchar_t *string_get_arg_argv(int *argidx, wchar_t **argv) { - return (argv && argv[*argidx]) ? argv[(*argidx)++] : 0; + return argv && argv[*argidx] ? argv[(*argidx)++] : 0; } static const wchar_t *string_get_arg(int *argidx, wchar_t **argv, wcstring *storage, const io_streams_t &streams) { if (string_args_from_stdin(streams)) { return string_get_arg_stdin(storage, streams); - } else { - return string_get_arg_argv(argidx, argv); } + return string_get_arg_argv(argidx, argv); } static int string_escape(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv) { @@ -138,7 +136,7 @@ static int string_escape(parser_t &parser, io_streams_t &streams, int argc, wcha nesc++; } - return (nesc > 0) ? BUILTIN_STRING_OK : BUILTIN_STRING_NONE; + return nesc > 0 ? BUILTIN_STRING_OK : BUILTIN_STRING_NONE; } static int string_join(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv) { @@ -195,7 +193,7 @@ static int string_join(parser_t &parser, io_streams_t &streams, int argc, wchar_ streams.out.push_back(L'\n'); } - return (nargs > 1) ? BUILTIN_STRING_OK : BUILTIN_STRING_NONE; + return nargs > 1 ? BUILTIN_STRING_OK : BUILTIN_STRING_NONE; } static int string_length(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv) { @@ -245,7 +243,7 @@ static int string_length(parser_t &parser, io_streams_t &streams, int argc, wcha } } - return (nnonempty > 0) ? BUILTIN_STRING_OK : BUILTIN_STRING_NONE; + return nnonempty > 0 ? BUILTIN_STRING_OK : BUILTIN_STRING_NONE; } struct match_options_t { @@ -923,7 +921,7 @@ static int string_split(parser_t &parser, io_streams_t &streams, int argc, wchar } // We split something if we have more split values than args. - return (splits.size() > arg_count) ? BUILTIN_STRING_OK : BUILTIN_STRING_NONE; + return splits.size() > arg_count ? BUILTIN_STRING_OK : BUILTIN_STRING_NONE; } static int string_sub(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv) { @@ -1028,7 +1026,7 @@ static int string_sub(parser_t &parser, io_streams_t &streams, int argc, wchar_t nsub++; } - return (nsub > 0) ? BUILTIN_STRING_OK : BUILTIN_STRING_NONE; + return nsub > 0 ? BUILTIN_STRING_OK : BUILTIN_STRING_NONE; } static int string_trim(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv) { @@ -1118,7 +1116,7 @@ static int string_trim(parser_t &parser, io_streams_t &streams, int argc, wchar_ } } - return (ntrim > 0) ? BUILTIN_STRING_OK : BUILTIN_STRING_NONE; + return ntrim > 0 ? BUILTIN_STRING_OK : BUILTIN_STRING_NONE; } static const struct string_subcommand { diff --git a/src/builtin_test.cpp b/src/builtin_test.cpp index 16d0209a1..c179952e0 100644 --- a/src/builtin_test.cpp +++ b/src/builtin_test.cpp @@ -281,12 +281,10 @@ expression *test_parser::parse_unary_expression(unsigned int start, unsigned int 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); + return NULL; } + return parse_primary(start, end); } /// Parse a combining expression (AND, OR). @@ -330,14 +328,12 @@ expression *test_parser::parse_combining_expression(unsigned int start, unsigned first = false; } - 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; + if (subjects.empty()) { + return NULL; // no subjects } + // 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); } expression *test_parser::parse_unary_primary(unsigned int start, unsigned int end) { @@ -824,18 +820,18 @@ int builtin_test(parser_t &parser, io_streams_t &streams, wchar_t **argv) { #endif streams.err.append(err); return BUILTIN_TEST_FAIL; - } else { - wcstring_list_t eval_errors; - bool result = expr->evaluate(eval_errors); - if (!eval_errors.empty()) { - printf("test returned eval errors:\n"); - for (size_t i = 0; i < eval_errors.size(); i++) { - printf("\t%ls\n", eval_errors.at(i).c_str()); - } - } - delete expr; - return result ? BUILTIN_TEST_SUCCESS : BUILTIN_TEST_FAIL; } + + wcstring_list_t eval_errors; + bool result = expr->evaluate(eval_errors); + if (!eval_errors.empty()) { + printf("test returned eval errors:\n"); + for (size_t i = 0; i < eval_errors.size(); i++) { + printf("\t%ls\n", eval_errors.at(i).c_str()); + } + } + delete expr; + return result ? BUILTIN_TEST_SUCCESS : BUILTIN_TEST_FAIL; } } return 1; diff --git a/src/color.cpp b/src/color.cpp index 7ab96779b..7648449d9 100644 --- a/src/color.cpp +++ b/src/color.cpp @@ -283,13 +283,9 @@ color24_t rgb_color_t::to_color24() const { unsigned char rgb_color_t::to_name_index() const { assert(type == type_named || type == type_rgb); - if (type == type_named) { - return data.name_idx; - } else if (type == type_rgb) { - return term8_color_for_rgb(data.color.rgb); - } else { - return (unsigned char)-1; // this is an error - } + if (type == type_named) return data.name_idx; + if (type == type_rgb) return term8_color_for_rgb(data.color.rgb); + return (unsigned char)-1; // this is an error } void rgb_color_t::parse(const wcstring &str) { diff --git a/src/common.cpp b/src/common.cpp index eedcd1c99..834f7b8b3 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -194,20 +194,15 @@ char *wcs2str(const wchar_t *in) { if (result) { // It converted into the local buffer, so copy it. result = strdup(result); - if (!result) { - DIE_MEM(); - } + if (!result) DIE_MEM(); } return result; - - } else { - // Here we probably allocate a buffer probably much larger than necessary. - char *out = (char *)malloc(MAX_UTF8_BYTES * wcslen(in) + 1); - if (!out) { - DIE_MEM(); - } - return wcs2str_internal(in, out); } + + // Here we probably allocate a buffer probably much larger than necessary. + char *out = (char *)malloc(MAX_UTF8_BYTES * wcslen(in) + 1); + if (!out) DIE_MEM(); + return wcs2str_internal(in, out); } char *wcs2str(const wcstring &in) { return wcs2str(in.c_str()); } @@ -411,10 +406,8 @@ wcstring wsetlocale(int category, const wchar_t *locale) { // U+23CE is the "return" character omitted_newline_char = (wcwidth(L'\x23CE') > 0) ? L'\x23CE' : L'~'; - if (!res) - return wcstring(); - else - return format_string(L"%s", res); + if (!res) return wcstring(); + return format_string(L"%s", res); } bool contains_internal(const wchar_t *a, int vararg_handle, ...) { diff --git a/src/complete.cpp b/src/complete.cpp index 198ad2e31..ef713bb8f 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -71,9 +71,8 @@ void complete_set_variable_names(const wcstring_list_t *names) { static inline wcstring_list_t complete_get_variable_names(void) { if (s_override_variable_names != NULL) { return *s_override_variable_names; - } else { - return env_get_names(0); } + return env_get_names(0); } /// Struct describing a completion option entry. @@ -161,9 +160,8 @@ struct completion_entry_set_comparer { // Paths always come last for no particular reason. if (p1.cmd_is_path != p2.cmd_is_path) { return p1.cmd_is_path < p2.cmd_is_path; - } else { - return p1.cmd < p2.cmd; } + return p1.cmd < p2.cmd; } }; typedef std::set completion_entry_set_t; @@ -285,8 +283,7 @@ class completer_t { enum complete_type_t { COMPLETE_DEFAULT, COMPLETE_AUTOSUGGEST }; complete_type_t type() const { - return (flags & COMPLETION_REQUEST_AUTOSUGGESTION) ? COMPLETE_AUTOSUGGEST - : COMPLETE_DEFAULT; + return flags & COMPLETION_REQUEST_AUTOSUGGESTION ? COMPLETE_AUTOSUGGEST : COMPLETE_DEFAULT; } bool wants_descriptions() const { return !!(flags & COMPLETION_REQUEST_DESCRIPTIONS); } @@ -295,8 +292,8 @@ class completer_t { fuzzy_match_type_t max_fuzzy_match_type() const { // If we are doing fuzzy matching, request all types; if not request only prefix matching. - return (flags & COMPLETION_REQUEST_FUZZY_MATCH) ? fuzzy_match_none - : fuzzy_match_prefix_case_insensitive; + if (flags & COMPLETION_REQUEST_FUZZY_MATCH) return fuzzy_match_none; + return fuzzy_match_prefix_case_insensitive; } public: @@ -1313,7 +1310,7 @@ void complete(const wcstring &cmd_with_subcmds, std::vector *out_c tree.get_child(*plain_statement, 0, parse_token_type_string); // Get the actual command string. - if (cmd_node != NULL) current_command = cmd_node->get_source(cmd); + if (cmd_node) current_command = cmd_node->get_source(cmd); // Check the decoration. switch (tree.decoration_for_plain_statement(*plain_statement)) { @@ -1341,7 +1338,7 @@ void complete(const wcstring &cmd_with_subcmds, std::vector *out_c } } - if (cmd_node && cmd_node->location_in_or_at_end_of_source_range(pos)) { + if (cmd_node && cmd_node->location_in_or_at_end_of_source_range(pos)) { // Complete command filename. completer.complete_cmd(current_token, use_function, use_builtin, use_command, use_implicit_cd); diff --git a/src/env.cpp b/src/env.cpp index e4829a463..6d5e2ab87 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -609,9 +609,8 @@ static bool try_remove(env_node_t *n, const wchar_t *key, int var_mode) { if (n->new_scope) { return try_remove(global_env, key, var_mode); - } else { - return try_remove(n->next, key, var_mode); } + return try_remove(n->next, key, var_mode); } int env_remove(const wcstring &key, int var_mode) { @@ -713,9 +712,8 @@ env_var_t env_get_string(const wcstring &key, env_mode_flags_t mode) { if (entry != NULL && (entry->exportv ? search_exported : search_unexported)) { if (entry->val == ENV_NULL) { return env_var_t::missing_var(); - } else { - return entry->val; } + return entry->val; } if (has_scope) { @@ -1074,10 +1072,9 @@ env_var_t env_vars_snapshot_t::get(const wcstring &key) const { // If we represent the current state, bounce to env_get_string. if (this->is_current()) { return env_get_string(key); - } else { - std::map::const_iterator iter = vars.find(key); - return (iter == vars.end() ? env_var_t::missing_var() : env_var_t(iter->second)); } + std::map::const_iterator iter = vars.find(key); + return iter == vars.end() ? env_var_t::missing_var() : env_var_t(iter->second); } const wchar_t *const env_vars_snapshot_t::highlighting_keys[] = {L"PATH", L"CDPATH", diff --git a/src/env_universal_common.cpp b/src/env_universal_common.cpp index fc78a1ceb..bb5b759be 100644 --- a/src/env_universal_common.cpp +++ b/src/env_universal_common.cpp @@ -85,7 +85,10 @@ static wcstring vars_filename_in_directory(const wcstring &wdir) { } static const wcstring &default_vars_path() { - static wcstring cached_result = vars_filename_in_directory(fishd_get_config()); + // Oclint complains about this being a "redundant local variable"; however it isn't because the + // assignment to a static var is needed to keep the object from being deleted when this function + // returns. + static wcstring cached_result = vars_filename_in_directory(fishd_get_config()); //!OCLINT return cached_result; } @@ -1121,9 +1124,8 @@ class universal_notifier_shmem_poller_t : public universal_notifier_t { unsigned long usec_per_sec = 1000000; if (get_time() - last_change_time < 5LL * usec_per_sec) { return usec_per_sec / 10; // 10 times a second - } else { - return usec_per_sec / 3; // 3 times a second } + return usec_per_sec / 3; // 3 times a second } }; @@ -1287,10 +1289,9 @@ class universal_notifier_named_pipe_t : public universal_notifier_t { // We are in polling mode because we think our fd is readable. This means that, if we // return it to be select()'d on, we'll be called back immediately. So don't return it. return -1; - } else { - // We are not in polling mode. Return the fd so it can be watched. - return pipe_fd; } + // We are not in polling mode. Return the fd so it can be watched. + return pipe_fd; } bool notification_fd_became_readable(int fd) { diff --git a/src/event.cpp b/src/event.cpp index 26579101f..3f5d05b9e 100644 --- a/src/event.cpp +++ b/src/event.cpp @@ -229,9 +229,8 @@ static wcstring event_desc_compact(const event_t &event) { } if (event.function_name.size()) { return format_string(L"%ls: \"%ls\"", res.c_str(), event.function_name.c_str()); - } else { - return res; } + return res; } void event_add_handler(const event_t &event) { diff --git a/src/exec.cpp b/src/exec.cpp index b55e7a26e..79f461d19 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -145,13 +145,14 @@ char *get_interpreter(const char *command, char *interpreter, size_t buff_size) interpreter[idx++] = '\0'; close(fd); } + if (strncmp(interpreter, "#! /", 4) == 0) { return interpreter + 3; } else if (strncmp(interpreter, "#!/", 3) == 0) { return interpreter + 2; - } else { - return NULL; } + + return NULL; } /// This function is executed by the child process created by a call to fork(). It should be called diff --git a/src/expand.cpp b/src/expand.cpp index d64b3cbcc..f8cd75508 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -907,54 +907,54 @@ static int expand_variables(const wcstring &instr, std::vector *ou } return is_ok; - } else { - // Even with no value, we still need to parse out slice syntax. Behave as though we - // had 1 value, so $foo[1] always works. - const size_t slice_start = stop_pos; - if (slice_start < insize && instr.at(slice_start) == L'[') { - const wchar_t *in = instr.c_str(); - wchar_t *slice_end; - size_t bad_pos; + } - bad_pos = - parse_slice(in + slice_start, &slice_end, var_idx_list, var_pos_list, 1); - if (bad_pos != 0) { - append_syntax_error(errors, stop_pos + bad_pos, L"Invalid index value"); + // Even with no value, we still need to parse out slice syntax. Behave as though we + // had 1 value, so $foo[1] always works. + const size_t slice_start = stop_pos; + if (slice_start < insize && instr.at(slice_start) == L'[') { + const wchar_t *in = instr.c_str(); + wchar_t *slice_end; + size_t bad_pos; + + bad_pos = + parse_slice(in + slice_start, &slice_end, var_idx_list, var_pos_list, 1); + if (bad_pos != 0) { + append_syntax_error(errors, stop_pos + bad_pos, L"Invalid index value"); + is_ok = 0; + return is_ok; + } + stop_pos = (slice_end - in); + + // Validate that the parsed indexes are valid. + for (size_t j = 0; j < var_idx_list.size(); j++) { + long tmp = var_idx_list.at(j); + if (tmp != 1) { + size_t var_src_pos = var_pos_list.at(j); + append_syntax_error(errors, slice_start + var_src_pos, + ARRAY_BOUNDS_ERR); is_ok = 0; return is_ok; } - stop_pos = (slice_end - in); - - // Validate that the parsed indexes are valid. - for (size_t j = 0; j < var_idx_list.size(); j++) { - long tmp = var_idx_list.at(j); - if (tmp != 1) { - size_t var_src_pos = var_pos_list.at(j); - append_syntax_error(errors, slice_start + var_src_pos, - ARRAY_BOUNDS_ERR); - is_ok = 0; - return is_ok; - } - } } + } - // Expand a non-existing variable. - if (c == VARIABLE_EXPAND) { - // Regular expansion, i.e. expand this argument to nothing. - empty = true; - } else { - // Expansion to single argument. - wcstring res; - res.append(instr, 0, i); - if (i > 0 && instr.at(i - 1) == VARIABLE_EXPAND_SINGLE) { - res.push_back(VARIABLE_EXPAND_EMPTY); - } - assert(stop_pos <= insize); - res.append(instr, stop_pos, insize - stop_pos); - - is_ok &= expand_variables(res, out, i, errors); - return is_ok; + // Expand a non-existing variable. + if (c == VARIABLE_EXPAND) { + // Regular expansion, i.e. expand this argument to nothing. + empty = true; + } else { + // Expansion to single argument. + wcstring res; + res.append(instr, 0, i); + if (i > 0 && instr.at(i - 1) == VARIABLE_EXPAND_SINGLE) { + res.push_back(VARIABLE_EXPAND_EMPTY); } + assert(stop_pos <= insize); + res.append(instr, stop_pos, insize - stop_pos); + + is_ok &= expand_variables(res, out, i, errors); + return is_ok; } } } @@ -1113,25 +1113,25 @@ static int expand_cmdsubst(const wcstring &input, std::vector *out if (bad_pos != 0) { append_syntax_error(errors, slice_begin - in + bad_pos, L"Invalid index value"); return 0; - } else { - wcstring_list_t 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()) { - size_t pos = slice_source_positions.at(i); - append_syntax_error(errors, slice_begin - in + pos, 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; } + + wcstring_list_t 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()) { + size_t pos = slice_source_positions.at(i); + append_syntax_error(errors, slice_begin - in + pos, 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; } // Recursively call ourselves to expand any remaining command substitutions. The result of this @@ -1385,9 +1385,8 @@ static expand_error_t expand_stage_home_and_pid(const wcstring &input, if (!next.empty() && next.at(0) == PROCESS_EXPAND) { expand_pid(next, flags, out, NULL); return EXPAND_OK; - } else { - append_completion(out, next); } + append_completion(out, next); } else if (!expand_pid(next, flags, out, errors)) { return EXPAND_ERROR; } @@ -1399,7 +1398,7 @@ static expand_error_t expand_stage_wildcards(const wcstring &input, std::vector< expand_error_t result = EXPAND_OK; wcstring path_to_expand = input; - remove_internal_separator(&path_to_expand, (EXPAND_SKIP_WILDCARDS & flags) ? true : false); + remove_internal_separator(&path_to_expand, flags & EXPAND_SKIP_WILDCARDS); const bool has_wildcard = wildcard_has(path_to_expand, true /* internal, i.e. ANY_CHAR */); if (has_wildcard && (flags & EXECUTABLES_ONLY)) { diff --git a/src/fallback.cpp b/src/fallback.cpp index 7dcaaee77..a5cff93d2 100644 --- a/src/fallback.cpp +++ b/src/fallback.cpp @@ -658,15 +658,15 @@ __attribute__((unused)) static wchar_t *wcsdup_fallback(const wchar_t *in) { __attribute__((unused)) static int wcscasecmp_fallback(const wchar_t *a, const wchar_t *b) { if (*a == 0) { - return (*b == 0) ? 0 : -1; + return *b == 0 ? 0 : -1; } else if (*b == 0) { return 1; } int diff = towlower(*a) - towlower(*b); - if (diff != 0) + if (diff != 0) { return diff; - else - return wcscasecmp_fallback(a + 1, b + 1); + } + return wcscasecmp_fallback(a + 1, b + 1); } __attribute__((unused)) static int wcsncasecmp_fallback(const wchar_t *a, const wchar_t *b, @@ -674,15 +674,13 @@ __attribute__((unused)) static int wcsncasecmp_fallback(const wchar_t *a, const if (count == 0) return 0; if (*a == 0) { - return (*b == 0) ? 0 : -1; + 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_fallback(a + 1, b + 1, count - 1); + if (diff != 0) return diff; + return wcsncasecmp_fallback(a + 1, b + 1, count - 1); } #if __APPLE__ && __DARWIN_C_LEVEL >= 200809L @@ -827,7 +825,7 @@ size_t wcslcat(wchar_t *dst, const wchar_t *src, size_t siz) { 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) { @@ -838,7 +836,7 @@ size_t wcslcat(wchar_t *dst, const wchar_t *src, size_t siz) { } *d = '\0'; - return (dlen + (s - src)); + return dlen + (s - src); /* count does not include NUL */ } @@ -882,7 +880,7 @@ size_t wcslcpy(wchar_t *dst, const wchar_t *src, size_t siz) { while (*s++) ; } - return (s - src - 1); + return s - src - 1; // Count does not include NUL. } diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index d0be58055..8bf58e1e6 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -3963,6 +3963,6 @@ int main(int argc, char **argv) { proc_destroy(); if (err_count != 0) { - return (1); + return 1; } } diff --git a/src/function.cpp b/src/function.cpp index 7d0b797b1..f717a16fe 100644 --- a/src/function.cpp +++ b/src/function.cpp @@ -230,9 +230,8 @@ static const function_info_t *function_get(const wcstring &name) { function_map_t::iterator iter = loaded_functions.find(name); if (iter == loaded_functions.end()) { return NULL; - } else { - return &iter->second; } + return &iter->second; } bool function_get_definition(const wcstring &name, wcstring *out_definition) { diff --git a/src/highlight.cpp b/src/highlight.cpp index a92edae30..f34505cf1 100644 --- a/src/highlight.cpp +++ b/src/highlight.cpp @@ -214,8 +214,7 @@ 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); - return result; + return is_potential_path(path, directories, flags | PATH_REQUIRE_DIR); } // Given a plain statement node in a parse tree, get the command and return it, expanded diff --git a/src/history.cpp b/src/history.cpp index f7f3affc6..6f38eb016 100644 --- a/src/history.cpp +++ b/src/history.cpp @@ -442,17 +442,17 @@ static size_t offset_of_next_item_fish_1_x(const char *begin, size_t mmap_length break; } case '\n': { - if (ignore_newline) { - ignore_newline = false; - } else { + if (!ignore_newline) { // Note: pos will be left pointing just after this newline, because of the ++ in // the loop. all_done = true; } + ignore_newline = false; break; } } } + *inout_cursor = (pos - begin); return result; } @@ -707,11 +707,11 @@ static size_t read_line(const char *base, size_t cursor, size_t len, std::string result.assign(start, newline - start); // Return the amount to advance the cursor; skip over the newline. return newline - start + 1; - } else { - // We ran off the end. - result.clear(); - return len - cursor; } + + // We ran off the end. + result.clear(); + return len - cursor; } /// Trims leading spaces in the given string, returning how many there were. diff --git a/src/input.cpp b/src/input.cpp index 0740a113e..efe2f5946 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -535,13 +535,12 @@ static bool input_mapping_is_match(const input_mapping_t &m) { // m.command.c_str()); // We matched the entire sequence. return true; - } else { - int k; - // Return the read characters. - input_common_next_ch(c); - for (k = j - 1; k >= 0; k--) { - input_common_next_ch(m.seq[k]); - } + } + + // Return the read characters. + input_common_next_ch(c); + for (int k = j - 1; k >= 0; k--) { + input_common_next_ch(m.seq[k]); } return false; @@ -630,12 +629,12 @@ wint_t input_readch(bool allow_commands) { case R_AND: { if (input_function_status) { return input_readch(); - } else { - while ((c = input_common_readch(0)) && c >= R_MIN && c <= R_MAX) - ; - input_common_next_ch(c); - return input_readch(); } + while ((c = input_common_readch(0)) && c >= R_MIN && c <= R_MAX) { + // do nothing + } + input_common_next_ch(c); + return input_readch(); } default: { return c; } } diff --git a/src/kill.cpp b/src/kill.cpp index c846f5f55..c3b1000e0 100644 --- a/src/kill.cpp +++ b/src/kill.cpp @@ -92,10 +92,9 @@ const wchar_t *kill_yank_rotate() { // Move the first element to the end. if (kill_list.empty()) { return NULL; - } else { - kill_list.splice(kill_list.end(), kill_list, kill_list.begin()); - return kill_list.front().c_str(); } + kill_list.splice(kill_list.end(), kill_list, kill_list.begin()); + return kill_list.front().c_str(); } /// Check the X clipboard. If it has been changed, add the new clipboard contents to the fish @@ -132,9 +131,8 @@ const wchar_t *kill_yank() { kill_check_x_buffer(); if (kill_list.empty()) { return L""; - } else { - return kill_list.front().c_str(); } + return kill_list.front().c_str(); } void kill_sanity_check() {} diff --git a/src/output.cpp b/src/output.cpp index 935efe96c..78ea210bb 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -58,9 +58,8 @@ void output_set_color_support(color_support_t val) { color_support = val; } unsigned char index_for_color(rgb_color_t c) { if (c.is_named() || !(output_get_color_support() & color_support_term256)) { return c.to_name_index(); - } else { - return c.to_term256_index(); } + return c.to_term256_index(); } static bool write_color_escape(char *todo, unsigned char idx, bool is_fg) { @@ -95,9 +94,8 @@ static bool write_foreground_color(unsigned char idx) { return write_color_escape(set_a_foreground, idx, true); } else if (set_foreground && set_foreground[0]) { return write_color_escape(set_foreground, idx, true); - } else { - return false; } + return false; } static bool write_background_color(unsigned char idx) { @@ -105,9 +103,8 @@ static bool write_background_color(unsigned char idx) { return write_color_escape(set_a_background, idx, false); } else if (set_background && set_background[0]) { return write_color_escape(set_background, idx, false); - } else { - return false; } + return false; } void write_color(rgb_color_t color, bool is_fg) { diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp index a553f4cbf..13e499244 100644 --- a/src/parse_execution.cpp +++ b/src/parse_execution.cpp @@ -213,16 +213,18 @@ parse_execution_context_t::execution_cancellation_reason_t parse_execution_context_t::cancellation_reason(const block_t *block) const { if (shell_is_exiting()) { return execution_cancellation_exit; - } else if (parser && parser->cancellation_requested) { + } + if (parser && parser->cancellation_requested) { return execution_cancellation_skip; - } else if (block && block->loop_status != LOOP_NORMAL) { + } + if (block && block->loop_status != LOOP_NORMAL) { // Nasty hack - break and continue set the 'skip' flag as well as the loop status flag. return execution_cancellation_loop_control; - } else if (block && block->skip) { - return execution_cancellation_skip; - } else { - return execution_cancellation_none; } + if (block && block->skip) { + return execution_cancellation_skip; + } + return execution_cancellation_none; } /// Return whether the job contains a single statement, of block type, with no redirections. @@ -1042,10 +1044,9 @@ parse_execution_result_t parse_execution_context_t::populate_boolean_process( if (skip_job) { return parse_execution_skipped; - } else { - const parse_node_t &subject = *tree.get_child(bool_statement, 1, symbol_statement); - return this->populate_job_process(job, proc, subject); } + const parse_node_t &subject = *tree.get_child(bool_statement, 1, symbol_statement); + return this->populate_job_process(job, proc, subject); } parse_execution_result_t parse_execution_context_t::populate_block_process( diff --git a/src/parse_tree.cpp b/src/parse_tree.cpp index ef79082f3..1ec7aa439 100644 --- a/src/parse_tree.cpp +++ b/src/parse_tree.cpp @@ -1349,10 +1349,10 @@ static bool node_has_ancestor(const parse_node_tree_t &tree, const parse_node_t return true; // found it } else if (node.parent == NODE_OFFSET_INVALID) { return false; // no more parents - } else { - // Recurse to the parent. - return node_has_ancestor(tree, tree.at(node.parent), proposed_ancestor); } + + // Recurse to the parent. + return node_has_ancestor(tree, tree.at(node.parent), proposed_ancestor); } const parse_node_t *parse_node_tree_t::find_last_node_of_type(parse_token_type_t type, diff --git a/src/parse_util.cpp b/src/parse_util.cpp index f3868435e..d1d921893 100644 --- a/src/parse_util.cpp +++ b/src/parse_util.cpp @@ -67,7 +67,7 @@ size_t parse_util_get_offset_from_line(const wcstring &str, int line) { int count = 0; if (line < 0) { - return (size_t)(-1); + return (size_t)-1; } if (line == 0) return 0; @@ -779,8 +779,7 @@ bool parse_util_argument_is_help(const wchar_t *s, int min_match) { min_match = maxi(min_match, 3); - return (wcscmp(L"-h", s) == 0) || - (len >= (size_t)min_match && (wcsncmp(L"--help", s, len) == 0)); + return wcscmp(L"-h", s) == 0 || (len >= (size_t)min_match && (wcsncmp(L"--help", s, len) == 0)); } /// Check if the first argument under the given node is --help. @@ -1036,33 +1035,33 @@ parser_test_error_bits_t parse_util_detect_errors_in_argument(const parse_node_t working_copy.c_str()); } return 1; - } else { - // Check for invalid variable expansions. - const size_t unesc_size = unesc.size(); - for (size_t idx = 0; idx < unesc_size; idx++) { - switch (unesc.at(idx)) { - case VARIABLE_EXPAND: - case VARIABLE_EXPAND_SINGLE: { - wchar_t next_char = idx + 1 < unesc_size ? unesc.at(idx + 1) : L'\0'; + } - if (next_char != VARIABLE_EXPAND && next_char != VARIABLE_EXPAND_SINGLE && - !wcsvarchr(next_char)) { - err = 1; - if (out_errors) { - // We have something like $$$^.... Back up until we reach the first $. - size_t first_dollar = idx; - while (first_dollar > 0 && - (unesc.at(first_dollar - 1) == VARIABLE_EXPAND || - unesc.at(first_dollar - 1) == VARIABLE_EXPAND_SINGLE)) { - first_dollar--; - } - parse_util_expand_variable_error(unesc, node.source_start, first_dollar, - out_errors); + // Check for invalid variable expansions. + const size_t unesc_size = unesc.size(); + for (size_t idx = 0; idx < unesc_size; idx++) { + switch (unesc.at(idx)) { + case VARIABLE_EXPAND: + case VARIABLE_EXPAND_SINGLE: { + wchar_t next_char = idx + 1 < unesc_size ? unesc.at(idx + 1) : L'\0'; + + if (next_char != VARIABLE_EXPAND && next_char != VARIABLE_EXPAND_SINGLE && + !wcsvarchr(next_char)) { + err = 1; + if (out_errors) { + // We have something like $$$^.... Back up until we reach the first $. + size_t first_dollar = idx; + while (first_dollar > 0 && + (unesc.at(first_dollar - 1) == VARIABLE_EXPAND || + unesc.at(first_dollar - 1) == VARIABLE_EXPAND_SINGLE)) { + first_dollar--; } + parse_util_expand_variable_error(unesc, node.source_start, first_dollar, + out_errors); } - - break; } + + break; } } } diff --git a/src/parser.cpp b/src/parser.cpp index ff10ea8ed..47d1994f0 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -670,9 +670,8 @@ int parser_t::eval_block_node(node_offset_t node_idx, const io_chain_t &io, if (this->cancellation_requested) { if (!block_stack.empty()) { return 1; - } else { - this->cancellation_requested = false; } + this->cancellation_requested = false; } // Only certain blocks are allowed. diff --git a/src/path.cpp b/src/path.cpp index d80ffadfc..b5ee07902 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -22,71 +22,66 @@ static bool path_get_path_core(const wcstring &cmd, wcstring *out_path, const env_var_t &bin_path_var) { int err = ENOENT; - debug(3, L"path_get_path( '%ls' )", cmd.c_str()); // If the command has a slash, it must be a full path. if (cmd.find(L'/') != wcstring::npos) { - if (waccess(cmd, X_OK) == 0) { - struct stat buff; - if (wstat(cmd, &buff)) { - return false; - } - - if (S_ISREG(buff.st_mode)) { - if (out_path) out_path->assign(cmd); - return true; - } else { - errno = EACCES; - return false; - } - } else { + if (waccess(cmd, X_OK) != 0) { return false; } - } else { - wcstring bin_path; - 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"; - } + struct stat buff; + if (wstat(cmd, &buff)) { + return false; } + if (S_ISREG(buff.st_mode)) { + if (out_path) out_path->assign(cmd); + return true; + } + errno = EACCES; + return false; + } - wcstring nxt_path; - wcstokenizer tokenizer(bin_path, ARRAY_SEP_STR); - 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 (out_path) out_path->swap(nxt_path); - return true; - } - err = EACCES; + wcstring bin_path; + 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"; + } + } - } 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"); - } + wcstring nxt_path; + wcstokenizer tokenizer(bin_path, ARRAY_SEP_STR); + 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 (out_path) out_path->swap(nxt_path); + return true; + } + err = EACCES; + } else { + switch (errno) { + case ENOENT: + case ENAMETOOLONG: + case EACCES: + case ENOTDIR: { + break; + } + default: { + debug(1, MISSING_COMMAND_ERR_MSG, nxt_path.c_str()); + wperror(L"access"); } } } @@ -208,23 +203,23 @@ wcstring path_apply_working_directory(const wcstring &path, const wcstring &work if (!prepend_wd) { // No need to prepend the wd, so just return the path we were given. return path; - } else { - // Remove up to one "./". - wcstring path_component = path; - if (string_prefixes_string(L"./", path_component)) { - path_component.erase(0, 2); - } - - // Removing leading /s. - while (string_prefixes_string(L"/", path_component)) { - path_component.erase(0, 1); - } - - // Construct and return a new path. - wcstring new_path = working_directory; - append_path_component(new_path, path_component); - return new_path; } + + // Remove up to one "./". + wcstring path_component = path; + if (string_prefixes_string(L"./", path_component)) { + path_component.erase(0, 2); + } + + // Removing leading /s. + while (string_prefixes_string(L"/", path_component)) { + path_component.erase(0, 1); + } + + // Construct and return a new path. + wcstring new_path = working_directory; + append_path_component(new_path, path_component); + return new_path; } static wcstring path_create_config() { diff --git a/src/proc.cpp b/src/proc.cpp index 9bec1811b..305be0df9 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -175,12 +175,12 @@ job_id_t acquire_job_id(void) { // We found a slot. Note that slot 0 corresponds to job ID 1. *slot = true; return (job_id_t)(slot - consumed_job_ids.begin() + 1); - } else { - // We did not find a slot; create a new slot. The size of the vector is now the job ID - // (since it is one larger than the slot). - consumed_job_ids.push_back(true); - return (job_id_t)consumed_job_ids.size(); } + + // We did not find a slot; create a new slot. The size of the vector is now the job ID + // (since it is one larger than the slot). + consumed_job_ids.push_back(true); + return (job_id_t)consumed_job_ids.size(); } void release_job_id(job_id_t jid) { @@ -368,14 +368,14 @@ process_t::process_t() } process_t::~process_t() { - if (this->next != NULL) delete this->next; + delete this->next; } job_t::job_t(job_id_t jobid, const io_chain_t &bio) : block_io(bio), first_process(NULL), pgid(0), tmodes(), job_id(jobid), flags(0) {} job_t::~job_t() { - if (first_process != NULL) delete first_process; + delete first_process; release_job_id(job_id); } @@ -451,10 +451,9 @@ static int process_mark_finished_children(bool wants_await) { if (got_error) { return -1; - } else { - s_last_processed_sigchld_generation_count = local_count; - return processed_count; } + s_last_processed_sigchld_generation_count = local_count; + return processed_count; } /// This is called from a signal handler. The signal is always SIGCHLD. diff --git a/src/reader.cpp b/src/reader.cpp index 7b6234888..9f640c0c5 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -232,9 +232,8 @@ class reader_data_t { editable_line_t *active_edit_line() { if (this->is_navigating_pager_contents() && this->pager.is_search_field_shown()) { return &this->pager.search_field_line; - } else { - return &this->command_line; } + return &this->command_line; } /// Do what we need to do whenever our command line changes. @@ -1070,55 +1069,55 @@ wcstring completion_apply_to_command_line(const wcstring &val_str, complete_flag size_t new_cursor_pos = (begin - buff) + move_cursor; *inout_cursor_pos = new_cursor_pos; return sb; - } else { - wchar_t quote = L'\0'; - wcstring replaced; - if (do_escape) { - // Note that we ignore COMPLETE_DONT_ESCAPE_TILDES here. We get away with this because - // unexpand_tildes only operates on completions that have COMPLETE_REPLACES_TOKEN set, - // but we ought to respect them. - parse_util_get_parameter_info(command_line, cursor_pos, "e, NULL, NULL); - - // If the token is reported as unquoted, but ends with a (unescaped) quote, and we can - // modify the command line, then delete the trailing quote so that we can insert within - // the quotes instead of after them. See issue #552. - if (quote == L'\0' && !append_only && cursor_pos > 0) { - // The entire token is reported as unquoted...see if the last character is an - // unescaped quote. - wchar_t trailing_quote = unescaped_quote(command_line, cursor_pos - 1); - if (trailing_quote != L'\0') { - quote = trailing_quote; - back_into_trailing_quote = true; - } - } - - replaced = parse_util_escape_string_with_quote(val_str, quote); - } else { - replaced = val; - } - - size_t insertion_point = cursor_pos; - if (back_into_trailing_quote) { - // Move the character back one so we enter the terminal quote. - assert(insertion_point > 0); - insertion_point--; - } - - // Perform the insertion and compute the new location. - wcstring result = command_line; - result.insert(insertion_point, replaced); - size_t new_cursor_pos = - insertion_point + replaced.size() + (back_into_trailing_quote ? 1 : 0); - if (add_space) { - if (quote != L'\0' && unescaped_quote(command_line, insertion_point) != quote) { - // This is a quoted parameter, first print a quote. - result.insert(new_cursor_pos++, wcstring("e, 1)); - } - result.insert(new_cursor_pos++, L" "); - } - *inout_cursor_pos = new_cursor_pos; - return result; } + + wchar_t quote = L'\0'; + wcstring replaced; + if (do_escape) { + // Note that we ignore COMPLETE_DONT_ESCAPE_TILDES here. We get away with this because + // unexpand_tildes only operates on completions that have COMPLETE_REPLACES_TOKEN set, + // but we ought to respect them. + parse_util_get_parameter_info(command_line, cursor_pos, "e, NULL, NULL); + + // If the token is reported as unquoted, but ends with a (unescaped) quote, and we can + // modify the command line, then delete the trailing quote so that we can insert within + // the quotes instead of after them. See issue #552. + if (quote == L'\0' && !append_only && cursor_pos > 0) { + // The entire token is reported as unquoted...see if the last character is an + // unescaped quote. + wchar_t trailing_quote = unescaped_quote(command_line, cursor_pos - 1); + if (trailing_quote != L'\0') { + quote = trailing_quote; + back_into_trailing_quote = true; + } + } + + replaced = parse_util_escape_string_with_quote(val_str, quote); + } else { + replaced = val; + } + + size_t insertion_point = cursor_pos; + if (back_into_trailing_quote) { + // Move the character back one so we enter the terminal quote. + assert(insertion_point > 0); + insertion_point--; + } + + // Perform the insertion and compute the new location. + wcstring result = command_line; + result.insert(insertion_point, replaced); + size_t new_cursor_pos = + insertion_point + replaced.size() + (back_into_trailing_quote ? 1 : 0); + if (add_space) { + if (quote != L'\0' && unescaped_quote(command_line, insertion_point) != quote) { + // This is a quoted parameter, first print a quote. + result.insert(new_cursor_pos++, wcstring("e, 1)); + } + result.insert(new_cursor_pos++, L" "); + } + *inout_cursor_pos = new_cursor_pos; + return result; } /// Insert the string at the current cursor position. The function checks if the string is quoted or @@ -1926,7 +1925,7 @@ void reader_set_buffer(const wcstring &b, size_t pos) { } size_t reader_get_cursor_pos() { - if (!data) return (size_t)(-1); + if (!data) return (size_t)-1; return data->command_line.position; } @@ -2357,9 +2356,9 @@ static int can_read(int fd) { // // TODO: Actually implement the replacement as documented above. static int wchar_private(wchar_t c) { - return ((c >= RESERVED_CHAR_BASE && c < RESERVED_CHAR_END) || - (c >= ENCODE_DIRECT_BASE && c < ENCODE_DIRECT_END) || - (c >= INPUT_COMMON_BASE && c < INPUT_COMMON_END)); + return (c >= RESERVED_CHAR_BASE && c < RESERVED_CHAR_END) || + (c >= ENCODE_DIRECT_BASE && c < ENCODE_DIRECT_END) || + (c >= INPUT_COMMON_BASE && c < INPUT_COMMON_END); } /// Test if the specified character in the specified string is backslashed. pos may be at the end of diff --git a/src/reader.h b/src/reader.h index 4c5a511ea..df667e9ef 100644 --- a/src/reader.h +++ b/src/reader.h @@ -111,7 +111,7 @@ history_t *reader_get_history(void); void reader_set_buffer(const wcstring &b, size_t p); /// Get the current cursor position in the command line. If interactive mode is uninitialized, -/// return (size_t)(-1). +/// return (size_t)-1. size_t reader_get_cursor_pos(); /// Get the current selection range in the command line. Returns false if there is no active diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 746807eb7..3bc5a1525 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -167,12 +167,11 @@ void tokenizer_t::read_string() { TOK_CALL_ERROR(this, TOK_UNTERMINATED_ESCAPE, UNTERMINATED_ESCAPE_ERROR, error_location); return; - } else { - // Since we are about to increment tok->buff, decrement it first so the - // increment doesn't go past the end of the buffer. See issue #389. - this->buff--; - do_loop = 0; } + // Since we are about to increment tok->buff, decrement it first so the + // increment doesn't go past the end of the buffer. See issue #389. + this->buff--; + do_loop = 0; } this->buff++; @@ -502,17 +501,12 @@ void tokenizer_t::tok_next() { this->read_comment(); if (this->buff[0] == L'\n' && this->continue_line_after_comment) this->buff++; - return; - } else { - while (*(this->buff) != L'\n' && *(this->buff) != L'\0') this->buff++; - - if (this->buff[0] == L'\n' && this->continue_line_after_comment) this->buff++; } - while (my_iswspace(*(this->buff))) { - this->buff++; - } + while (*(this->buff) != L'\n' && *(this->buff) != L'\0') this->buff++; + if (this->buff[0] == L'\n' && this->continue_line_after_comment) this->buff++; + while (my_iswspace(*(this->buff))) this->buff++; } this->continue_line_after_comment = false; diff --git a/src/utf8.cpp b/src/utf8.cpp index 9c8bcf6d2..afd0c7642 100644 --- a/src/utf8.cpp +++ b/src/utf8.cpp @@ -238,9 +238,8 @@ static size_t utf8_to_wchar_internal(const char *in, size_t insize, utf8_wstring if (__wchar_forbitten(out_val) != 0) { if ((flags & UTF8_IGNORE_ERROR) == 0) { return 0; // forbidden character - } else { - skip = true; } + skip = true; } else if (out_val == _BOM && (flags & UTF8_SKIP_BOM) != 0) { skip = true; } @@ -286,10 +285,8 @@ static size_t wchar_to_utf8_internal(const utf8_wchar_t *in, size_t insize, char total = 0; for (; w < wlim; w++) { if (__wchar_forbitten(*w) != 0) { - if ((flags & UTF8_IGNORE_ERROR) == 0) - return 0; - else - continue; + if ((flags & UTF8_IGNORE_ERROR) == 0) return 0; + continue; } if (*w == _BOM && (flags & UTF8_SKIP_BOM) != 0) continue; @@ -298,18 +295,13 @@ static size_t wchar_to_utf8_internal(const utf8_wchar_t *in, size_t insize, char if (w_wide < 0) { if ((flags & UTF8_IGNORE_ERROR) == 0) return 0; continue; - } else if (w_wide <= 0x0000007f) - n = 1; - else if (w_wide <= 0x000007ff) - n = 2; - else if (w_wide <= 0x0000ffff) - n = 3; - else if (w_wide <= 0x001fffff) - n = 4; - else if (w_wide <= 0x03ffffff) - n = 5; - else /// if (w_wide <= 0x7fffffff) - n = 6; + } + if (w_wide <= 0x0000007f) n = 1; + else if (w_wide <= 0x000007ff) n = 2; + else if (w_wide <= 0x0000ffff) n = 3; + else if (w_wide <= 0x001fffff) n = 4; + else if (w_wide <= 0x03ffffff) n = 5; + else n = 6; /// if (w_wide <= 0x7fffffff) total += n; diff --git a/src/util.cpp b/src/util.cpp index 316bac68c..80c9771a4 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -50,7 +50,7 @@ int wcsfilecmp(const wchar_t *a, const wchar_t *b) { b = bend - 1; } else { int diff = towlower(*a) - towlower(*b); - if (diff != 0) return (diff > 0) ? 2 : -2; + if (diff != 0) return diff > 0 ? 2 : -2; secondary_diff = *a - *b; } diff --git a/src/wcstringutil.cpp b/src/wcstringutil.cpp index 3983eb53a..9fcb168ee 100644 --- a/src/wcstringutil.cpp +++ b/src/wcstringutil.cpp @@ -22,8 +22,8 @@ wcstring_range wcstring_tok(wcstring& str, const wcstring& needle, wcstring_rang size_type next_pos = str.find_first_of(needle, pos); if (next_pos == wcstring::npos) { return std::make_pair(pos, wcstring::npos); - } else { - str[next_pos] = L'\0'; - return std::make_pair(pos, next_pos - pos); } + + str[next_pos] = L'\0'; + return std::make_pair(pos, next_pos - pos); } diff --git a/src/wildcard.cpp b/src/wildcard.cpp index 93242570e..ace52a798 100644 --- a/src/wildcard.cpp +++ b/src/wildcard.cpp @@ -150,14 +150,13 @@ static wcstring resolve_description(wcstring *completion, const wchar_t *explici const wcstring description = completion->substr(complete_sep_loc + 1); completion->resize(complete_sep_loc); return description; - } else { - const wcstring func_result = (desc_func ? desc_func(*completion) : wcstring()); - if (!func_result.empty()) { - return func_result; - } else { - return explicit_desc ? explicit_desc : L""; - } } + + const wcstring func_result = (desc_func ? desc_func(*completion) : wcstring()); + if (!func_result.empty()) { + return func_result; + } + return explicit_desc ? explicit_desc : L""; } // A transient parameter pack needed by wildcard_complete. @@ -240,64 +239,60 @@ static bool wildcard_complete_internal(const wchar_t *str, const wchar_t *wc, // Normal match. return wildcard_complete_internal(str + next_wc_char_pos, wc + next_wc_char_pos, params, flags, out); - } else if (wcsncasecmp(str, wc, next_wc_char_pos) == 0) { + } + if (wcsncasecmp(str, wc, next_wc_char_pos) == 0) { // Case insensitive match. return wildcard_complete_internal(str + next_wc_char_pos, wc + next_wc_char_pos, params, flags | COMPLETE_REPLACES_TOKEN, out); - } else { - // No match. - return false; } - assert(0 && "Unreachable code reached"); - } else { - // Our first character is a wildcard. - assert(next_wc_char_pos == 0); - switch (wc[0]) { - case ANY_CHAR: { - if (str[0] == L'\0') { - return false; - } else { - return wildcard_complete_internal(str + 1, wc + 1, params, flags, out); - } - break; + return false; // no match + } + + // Our first character is a wildcard. + assert(next_wc_char_pos == 0); + switch (wc[0]) { + case ANY_CHAR: { + if (str[0] == L'\0') { + return false; + } + return wildcard_complete_internal(str + 1, wc + 1, params, flags, out); + } + case ANY_STRING: { + // Hackish. If this is the last character of the wildcard, then just complete with + // the empty string. This fixes cases like "f*" -> "f*o". + if (wc[1] == L'\0') { + return wildcard_complete_internal(L"", L"", params, flags, out); } - case ANY_STRING: { - // Hackish. If this is the last character of the wildcard, then just complete with - // the empty string. This fixes cases like "f*" -> "f*o". - if (wc[1] == L'\0') { - return wildcard_complete_internal(L"", L"", params, flags, out); - } - // Try all submatches. Issue #929: if the recursive call gives us a prefix match, - // just stop. This is sloppy - what we really want to do is say, once we've seen a - // match of a particular type, ignore all matches of that type further down the - // string, such that the wildcard produces the "minimal match.". - bool has_match = false; - for (size_t i = 0; str[i] != L'\0'; i++) { - const size_t before_count = out ? out->size() : 0; - if (wildcard_complete_internal(str + i, wc + 1, params, flags, out)) { - // We found a match. - has_match = true; + // Try all submatches. Issue #929: if the recursive call gives us a prefix match, + // just stop. This is sloppy - what we really want to do is say, once we've seen a + // match of a particular type, ignore all matches of that type further down the + // string, such that the wildcard produces the "minimal match.". + bool has_match = false; + for (size_t i = 0; str[i] != L'\0'; i++) { + const size_t before_count = out ? out->size() : 0; + if (wildcard_complete_internal(str + i, wc + 1, params, flags, out)) { + // We found a match. + has_match = true; - // If out is NULL, we don't care about the actual matches. If out is not - // NULL but we have a prefix match, stop there. - if (out == NULL || has_prefix_match(out, before_count)) { - break; - } + // If out is NULL, we don't care about the actual matches. If out is not + // NULL but we have a prefix match, stop there. + if (out == NULL || has_prefix_match(out, before_count)) { + break; } } - return has_match; - } - case ANY_STRING_RECURSIVE: { - // We don't even try with this one. - return false; - } - default: { - assert(0 && "Unreachable code reached"); - return false; } + return has_match; + } + case ANY_STRING_RECURSIVE: { + // We don't even try with this one. + return false; + } + default: { + assert(0 && "Unreachable code reached"); } } + assert(0 && "Unreachable code reached"); } @@ -333,33 +328,32 @@ static wcstring file_get_desc(const wcstring &filename, int lstat_res, const str if (!stat_res) { if (S_ISDIR(buf.st_mode)) { return COMPLETE_DIRECTORY_SYMLINK_DESC; - } else { - if (buf.st_mode & (S_IXUSR | S_IXGRP | 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; - } + } + if (buf.st_mode & (S_IXUSR | S_IXGRP | 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. } + switch (err) { + case ENOENT: { + return COMPLETE_ROTTEN_SYMLINK_DESC; + } + case ELOOP: { + return COMPLETE_LOOP_SYMLINK_DESC; + } + default: { + // 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)) { @@ -449,9 +443,8 @@ static bool wildcard_test_flags_then_complete(const wcstring &filepath, const wc if (is_directory) { return wildcard_complete(filename + L'/', wc, desc.c_str(), NULL, out, expand_flags, COMPLETE_NO_SPACE); - } else { - return wildcard_complete(filename, wc, desc.c_str(), NULL, out, expand_flags, 0); } + return wildcard_complete(filename, wc, desc.c_str(), NULL, out, expand_flags, 0); } class wildcard_expander_t { @@ -637,9 +630,8 @@ class wildcard_expander_t { int status_code() const { if (this->did_interrupt) { return -1; - } else { - return this->did_add ? 1 : 0; } + return this->did_add ? 1 : 0; } }; diff --git a/src/wutil.cpp b/src/wutil.cpp index c8b9e3f48..4299956aa 100644 --- a/src/wutil.cpp +++ b/src/wutil.cpp @@ -292,24 +292,20 @@ const char *safe_strerror(int err) { return &_sys_errs[_sys_index[err]]; } #endif // either HAVE__SYS__ERRS or HAVE_SYS_ERRLIST - else #endif // defined(HAVE__SYS__ERRS) || defined(HAVE_SYS_ERRLIST) - { - int saved_err = errno; - // Use a shared buffer for this case. - static char buff[384]; - char errnum_buff[64]; - format_long_safe(errnum_buff, err); + int saved_err = errno; + static char buff[384]; // use a shared buffer for this case + char errnum_buff[64]; + format_long_safe(errnum_buff, err); - buff[0] = '\0'; - safe_append(buff, "unknown error (errno was ", sizeof buff); - safe_append(buff, errnum_buff, sizeof buff); - safe_append(buff, ")", sizeof buff); + buff[0] = '\0'; + safe_append(buff, "unknown error (errno was ", sizeof buff); + safe_append(buff, errnum_buff, sizeof buff); + safe_append(buff, ")", sizeof buff); - errno = saved_err; - return buff; - } + errno = saved_err; + return buff; } void safe_perror(const char *message) { From 22cc0515c98e58fc56a4bd82b6d6dbbcdfa71fcb Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Wed, 4 May 2016 16:49:06 -0700 Subject: [PATCH 223/363] another oclint rule to ignore --- .oclint | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.oclint b/.oclint index 4e9870e6e..4b5550ad4 100644 --- a/.oclint +++ b/.oclint @@ -8,6 +8,7 @@ rule-configurations: value: 100 disable-rules: + # # A few instances of "useless parentheses" errors are meaningful. Mostly # in the context of the `return` statement. Unfortunately the vast # majority would result in removing parentheses that decreases @@ -16,4 +17,13 @@ disable-rules: # # Also, some macro expansions, such as FD_SET(), trigger this warning and # we don't want to suppress each of those individually. + # - UselessParentheses + # + # OCLint wants variable names to be at least three characters in length. + # Which would be fine if it supported a reasonable set of exceptions + # (e.g., "i", "j", "k") and allowed adding additional exceptions to match + # conventions employed by a project. Since it doesn't, and thus generates + # a lot of really annoying warnings, we're going to disable this rule. + # + - ShortVariableName From 4246cfa95c516afe11d27ee7f5890f4f86d5f671 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Wed, 4 May 2016 16:55:47 -0700 Subject: [PATCH 224/363] config oclint to allow longer var names --- .oclint | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.oclint b/.oclint index 4b5550ad4..f38f47e67 100644 --- a/.oclint +++ b/.oclint @@ -1,11 +1,20 @@ rules: rule-configurations: + # # This is the default value (as of the time I wrote this) but I'm making # it explicit since it needs to agree with the value used by clang-format. # Thus, if we ever change the fish style to allow longer or shorter lines # this should be changed (as well as the corresponding .clang-format file). + # - key: LONG_LINE value: 100 + # + # The default limit for the length of variable names is 20. Long names are + # problematic but twenty chars results in way too many errors. So increase + # the limit to something more reasonable. + # + - key: LONG_VARIABLE_NAME + value: 30 disable-rules: # From d4b5620bb33c1ccce693a0dff93da8713e87f4d8 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Wed, 4 May 2016 18:08:26 -0700 Subject: [PATCH 225/363] config clang-format to ignore oclint pragmas --- .clang-format | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.clang-format b/.clang-format index d154da600..c540053d5 100644 --- a/.clang-format +++ b/.clang-format @@ -6,3 +6,5 @@ BasedOnStyle: Google ColumnLimit: 100 IndentWidth: 4 +# We don't want OCLint pragmas to be reformatted. +CommentPragmas: '^!OCLINT' From 1cdf3868226592e62d6214fec6572eeb476097d2 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Wed, 4 May 2016 18:14:04 -0700 Subject: [PATCH 226/363] lint: screen.cpp low hanging fruit The remaining lint work to be done on screen.cpp will require refactoring several functions that are way too large and complex. --- src/reader.cpp | 4 +- src/screen.cpp | 324 +++++++++++++++++++++++++------------------------ src/screen.h | 6 +- 3 files changed, 172 insertions(+), 162 deletions(-) diff --git a/src/reader.cpp b/src/reader.cpp index 9f640c0c5..bd4288711 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -429,8 +429,8 @@ static void reader_repaint() { size_t cursor_position = focused_on_pager ? data->pager.cursor_position() : cmd_line->position; s_write(&data->screen, data->left_prompt_buff, data->right_prompt_buff, full_line, - cmd_line->size(), &colors[0], &indents[0], cursor_position, data->sel_start_pos, - data->sel_stop_pos, data->current_page_rendering, focused_on_pager); + cmd_line->size(), &colors[0], &indents[0], cursor_position, + data->current_page_rendering, focused_on_pager); data->repaint_needed = false; } diff --git a/src/screen.cpp b/src/screen.cpp index d863448a7..882bd8d88 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -55,7 +55,7 @@ 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 character); /// Class to temporarily set s_writeb_buffer and the writer function in a scoped way. class scoped_buffer_t { @@ -87,20 +87,110 @@ static size_t try_sequence(const char *seq, const wchar_t *str) { } /// 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 current_line_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; + return ((current_line_width / tab_width) + 1) * tab_width; } /// Like fish_wcwidth, but returns 0 for control characters instead of -1. -static int fish_wcwidth_min_0(wchar_t wc) { return maxi(0, fish_wcwidth(wc)); } +static int fish_wcwidth_min_0(wchar_t widechar) { return maxi(0, fish_wcwidth(widechar)); } /// Whether we permit soft wrapping. If so, in some cases we don't explicitly move to the second /// physical line on a wrapped logical line; instead we just output it. static bool allow_soft_wrap(void) { // Should we be looking at eat_newline_glitch as well? - return !!auto_right_margin; + return auto_right_margin; +} + +/// Does this look like the escape sequence for setting a screen name. +static bool is_screen_name_escape_seq(const wchar_t *code, size_t *resulting_length) { + bool found = false; + if (code[1] == L'k') { + const env_var_t term_name = env_get_string(L"TERM"); + if (!term_name.missing() && string_prefixes_string(L"screen", term_name)) { + const wchar_t *const screen_name_end_sentinel = L"\x1b\\"; + const wchar_t *screen_name_end = wcsstr(&code[2], screen_name_end_sentinel); + if (screen_name_end == NULL) { + // Consider just k to be the code. + *resulting_length = 2; + } else { + const wchar_t *escape_sequence_end = + screen_name_end + wcslen(screen_name_end_sentinel); + *resulting_length = escape_sequence_end - code; + } + found = true; + } + } + return found; +} + +/// iTerm2 escape codes: CSI followed by ], terminated by either BEL or escape + backslash. +/// See https://code.google.com/p/iterm2/wiki/ProprietaryEscapeCodes. +static bool is_iterm2_escape_seq(const wchar_t *code, size_t *resulting_length) { + bool found = false; + if (code[1] == ']') { + // Start at 2 to skip over ]. + size_t cursor = 2; + for (; code[cursor] != L'\0'; cursor++) { + // Consume a sequence of characters up to \ or . + if (code[cursor] == '\x07' || (code[cursor] == '\\' && code[cursor - 1] == '\x1b')) { + found = true; + break; + } + } + if (found) { + *resulting_length = cursor + 1; + } + } + return found; +} + +/// Generic VT100 one byte sequence: CSI followed by something in the range @ through _. +static bool is_single_byte_escape_seq(const wchar_t *code, size_t *resulting_length) { + bool found = false; + if (code[1] == L'[' && (code[2] >= L'@' && code[2] <= L'_')) { + *resulting_length = 3; + found = true; + } + return found; +} + +/// Generic VT100 two byte sequence: followed by something in the range @ through _. +static bool is_two_byte_escape_seq(const wchar_t *code, size_t *resulting_length) { + bool found = false; + if (code[1] >= L'@' && code[1] <= L'_') { + *resulting_length = 2; + found = true; + } + return found; +} + +/// Generic VT100 CSI-style sequence. , followed by zero or more ASCII characters NOT in +/// the range [@,_], followed by one character in that range. +static bool is_csi_style_escape_seq(const wchar_t *code, size_t *resulting_length) { + bool found = false; + if (code[1] == L'[') { + // Start at 2 to skip over [ + size_t cursor = 2; + for (; code[cursor] != L'\0'; cursor++) { + // Consume a sequence of ASCII characters not in the range [@, ~]. + wchar_t widechar = code[cursor]; + + // If we're not in ASCII, just stop. + if (widechar > 127) break; + + // If we're the end character, then consume it and then stop. + if (widechar >= L'@' && widechar <= L'~') { + cursor++; + break; + } + } + // curs now indexes just beyond the end of the sequence (or at the terminating zero). + found = true; + *resulting_length = cursor; + } + return found; } /// Returns the number of characters in the escape code starting at 'code' (which should initially @@ -158,85 +248,11 @@ size_t escape_code_length(const wchar_t *code) { } } - if (!found) { - if (code[1] == L'k') { - // This looks like the escape sequence for setting a screen name. - const env_var_t term_name = env_get_string(L"TERM"); - if (!term_name.missing() && string_prefixes_string(L"screen", term_name)) { - const wchar_t *const screen_name_end_sentinel = L"\x1b\\"; - const wchar_t *screen_name_end = wcsstr(&code[2], screen_name_end_sentinel); - if (screen_name_end != NULL) { - const wchar_t *escape_sequence_end = - screen_name_end + wcslen(screen_name_end_sentinel); - resulting_length = escape_sequence_end - code; - } else { - // Consider just k to be the code. - resulting_length = 2; - } - found = true; - } - } - } - - if (!found) { - // iTerm2 escape codes: CSI followed by ], terminated by either BEL or escape + backslash. - // See https://code.google.com/p/iterm2/wiki/ProprietaryEscapeCodes. - if (code[1] == ']') { - // Start at 2 to skip over ]. - size_t cursor = 2; - for (; code[cursor] != L'\0'; cursor++) { - // Consume a sequence of characters up to \ or . - if (code[cursor] == '\x07' || - (code[cursor] == '\\' && code[cursor - 1] == '\x1b')) { - found = true; - break; - } - } - if (found) { - resulting_length = cursor + 1; - } - } - } - - if (!found) { - // Generic VT100 one byte sequence: CSI followed by something in the range @ through _. - if (code[1] == L'[' && (code[2] >= L'@' && code[2] <= L'_')) { - resulting_length = 3; - found = true; - } - } - - if (!found) { - // Generic VT100 CSI-style sequence. , followed by zero or more ASCII characters NOT in - // the range [@,_], followed by one character in that range. - if (code[1] == L'[') { - // Start at 2 to skip over [ - size_t cursor = 2; - for (; code[cursor] != L'\0'; cursor++) { - // Consume a sequence of ASCII characters not in the range [@, ~]. - wchar_t c = code[cursor]; - - // If we're not in ASCII, just stop. - if (c > 127) break; - - // If we're the end character, then consume it and then stop. - if (c >= L'@' && c <= L'~') { - cursor++; - break; - } - } - // curs now indexes just beyond the end of the sequence (or at the terminating zero). - found = true; - resulting_length = cursor; - } - } - if (!found) { - // Generic VT100 two byte sequence: followed by something in the range @ through _. - if (code[1] >= L'@' && code[1] <= L'_') { - resulting_length = 2; - found = true; - } - } + if (!found) found = is_screen_name_escape_seq(code, &resulting_length); + if (!found) found = is_iterm2_escape_seq(code, &resulting_length); + if (!found) found = is_single_byte_escape_seq(code, &resulting_length); + if (!found) found = is_csi_style_escape_seq(code, &resulting_length); + if (!found) found = is_two_byte_escape_seq(code, &resulting_length); return resulting_length; } @@ -364,55 +380,48 @@ static void s_check_status(screen_t *s) { static void s_desired_append_char(screen_t *s, wchar_t b, int c, int indent, size_t prompt_width) { int line_no = s->desired.cursor.y; - switch (b) { - case L'\n': { - int i; - // Current line is definitely hard wrapped. - s->desired.create_line(s->desired.line_count()); - s->desired.line(s->desired.cursor.y).is_soft_wrapped = false; + if (b == L'\n') { + int i; + // Current line is definitely hard wrapped. + s->desired.create_line(s->desired.line_count()); + s->desired.line(s->desired.cursor.y).is_soft_wrapped = false; + 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); + } + } else if (b == L'\r') { + line_t ¤t = s->desired.line(line_no); + current.clear(); + s->desired.cursor.x = 0; + } else { + int screen_width = common_get_width(); + int cw = fish_wcwidth_min_0(b); + + s->desired.create_line(line_no); + + // Check if we are at the end of the line. If so, continue on the next line. + if ((s->desired.cursor.x + cw) > screen_width) { + // 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 (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(); + + 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; - break; - } - default: { - int screen_width = common_get_width(); - int cw = fish_wcwidth_min_0(b); - - s->desired.create_line(line_no); - - // Check if we are at the end of the line. If so, continue on the next line. - if ((s->desired.cursor.x + cw) > screen_width) { - // 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; - } - - 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; + s->desired.cursor.y++; } } } @@ -457,7 +466,7 @@ static void s_move(screen_t *s, data_buffer_t *b, int new_x, int new_y) { s->screen_cursor[0], s->screen_cursor[1], new_x, new_y ); */ - scoped_buffer_t scoped_buffer(b); + scoped_buffer_t scoped_buffer(b); //!OCLINT(has side effects) y_steps = new_y - s->actual.cursor.y; @@ -513,7 +522,7 @@ static void s_move(screen_t *s, data_buffer_t *b, int new_x, int new_y) { /// Set the pen color for the terminal. static void s_set_color(screen_t *s, data_buffer_t *b, highlight_spec_t c) { - scoped_buffer_t scoped_buffer(b); + scoped_buffer_t scoped_buffer(b); //!OCLINT(has side effects) unsigned int uc = (unsigned int)c; set_color(highlight_get_color(uc & 0xffff, false), @@ -522,7 +531,7 @@ static void s_set_color(screen_t *s, data_buffer_t *b, highlight_spec_t c) { /// 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) { - scoped_buffer_t scoped_buffer(b); + scoped_buffer_t scoped_buffer(b); //!OCLINT(has side effects) s->actual.cursor.x += fish_wcwidth_min_0(c); writech(c); if (s->actual.cursor.x == s->actual_width && allow_soft_wrap()) { @@ -539,13 +548,13 @@ 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) { - scoped_buffer_t scoped_buffer(b); + scoped_buffer_t scoped_buffer(b); //!OCLINT(has side effects) 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) { - scoped_buffer_t scoped_buffer(b); + scoped_buffer_t scoped_buffer(b); //!OCLINT(has side effects) writestr(s); } @@ -573,7 +582,7 @@ static size_t line_shared_prefix(const line_t &a, const line_t &b) { // we believe we are already in the target position. This lets the terminal take care of wrapping, // which means that if you copy and paste the text, it won't have an embedded newline. static bool perform_any_impending_soft_wrap(screen_t *scr, int x, int y) { - if (x == scr->soft_wrap_location.x && y == scr->soft_wrap_location.y) { + if (x == scr->soft_wrap_location.x && y == scr->soft_wrap_location.y) { //!OCLINT // We can soft wrap; but do we want to? if (scr->desired.line(y - 1).is_soft_wrapped && allow_soft_wrap()) { // Yes. Just update the actual cursor; that will cause us to elide emitting the commands @@ -684,10 +693,12 @@ static void s_update(screen_t *scr, const wchar_t *left_prompt, const wchar_t *r // Determine how many lines have stuff on them; we need to clear lines with stuff that we don't // want. const size_t lines_with_stuff = maxi(actual_lines_before_reset, scr->actual.line_count()); +#if 0 if (lines_with_stuff > scr->desired.line_count()) { // There are lines that we output to previously that will need to be cleared. - // need_clear_lines = true; + need_clear_lines = true; } +#endif if (wcscmp(left_prompt, scr->actual_left_prompt.c_str())) { s_move(scr, &output, 0, 0); @@ -722,13 +733,13 @@ static void s_update(screen_t *scr, const wchar_t *left_prompt, const wchar_t *r // If we're soft wrapped, and if we're going to change the first character of the next // line, don't skip over the last two characters so that we maintain soft-wrapping. if (o_line.is_soft_wrapped && i + 1 < scr->desired.line_count()) { - bool first_character_of_next_line_will_change = true; - if (i + 1 < scr->actual.line_count()) { + bool next_line_will_change = true; + if (i + 1 < scr->actual.line_count()) { //!OCLINT if (line_shared_prefix(scr->desired.line(i + 1), scr->actual.line(i + 1)) > 0) { - first_character_of_next_line_will_change = false; + next_line_will_change = false; } } - if (first_character_of_next_line_will_change) { + if (next_line_will_change) { skip_remaining = mini(skip_remaining, (size_t)(scr->actual_width - 2)); } } @@ -933,12 +944,12 @@ static screen_layout_t compute_layout(screen_t *s, size_t screen_width, } // Compute the width of the autosuggestion at all possible truncation offsets. - std::vector autosuggestion_truncated_widths; - autosuggestion_truncated_widths.reserve(1 + wcslen(autosuggestion)); - size_t autosuggestion_total_width = 0; + std::vector autosuggest_truncated_widths; + autosuggest_truncated_widths.reserve(1 + wcslen(autosuggestion)); + size_t autosuggest_total_width = 0; for (size_t i = 0; autosuggestion[i] != L'\0'; i++) { - autosuggestion_truncated_widths.push_back(autosuggestion_total_width); - autosuggestion_total_width += fish_wcwidth_min_0(autosuggestion[i]); + autosuggest_truncated_widths.push_back(autosuggest_total_width); + autosuggest_total_width += fish_wcwidth_min_0(autosuggestion[i]); } // Here are the layouts we try in turn: @@ -962,7 +973,7 @@ static screen_layout_t compute_layout(screen_t *s, size_t screen_width, // Case 1 if (!done && left_prompt_width + right_prompt_width + first_command_line_width + - autosuggestion_total_width < + autosuggest_total_width < screen_width) { result.left_prompt = left_prompt; result.left_prompt_space = left_prompt_width; @@ -979,11 +990,11 @@ static screen_layout_t compute_layout(screen_t *s, size_t screen_width, result.right_prompt = right_prompt; // Need at least two characters to show an autosuggestion. - size_t available_autosuggestion_space = + size_t available_autosuggest_space = screen_width - (left_prompt_width + right_prompt_width + first_command_line_width); - if (autosuggestion_total_width > 0 && available_autosuggestion_space > 2) { - size_t truncation_offset = truncation_offset_for_width( - autosuggestion_truncated_widths, available_autosuggestion_space - 2); + if (autosuggest_total_width > 0 && available_autosuggest_space > 2) { + size_t truncation_offset = truncation_offset_for_width(autosuggest_truncated_widths, + available_autosuggest_space - 2); result.autosuggestion = wcstring(autosuggestion, truncation_offset); result.autosuggestion.push_back(ellipsis_char); } @@ -1019,8 +1030,8 @@ static screen_layout_t compute_layout(screen_t *s, size_t screen_width, void s_write(screen_t *s, const wcstring &left_prompt, const wcstring &right_prompt, const wcstring &commandline, size_t explicit_len, const highlight_spec_t *colors, - const int *indent, size_t cursor_pos, size_t sel_start_pos, size_t sel_stop_pos, - const page_rendering_t &pager, bool cursor_position_is_within_pager) { + const int *indent, size_t cursor_pos, const page_rendering_t &pager, + bool cursor_is_within_pager) { screen_data_t::cursor_t cursor_arr; CHECK(s, ); @@ -1082,7 +1093,7 @@ void s_write(screen_t *s, const wcstring &left_prompt, const wcstring &right_pro size_t i; for (i = 0; i < effective_commandline.size(); i++) { // Grab the current cursor's x,y position if this character matches the cursor's offset. - if (!cursor_position_is_within_pager && i == cursor_pos) { + if (!cursor_is_within_pager && i == cursor_pos) { cursor_arr = s->desired.cursor; } s_desired_append_char(s, effective_commandline.at(i), colors[i], indent[i], @@ -1090,7 +1101,7 @@ void s_write(screen_t *s, const wcstring &left_prompt, const wcstring &right_pro } // Cursor may have been at the end too. - if (!cursor_position_is_within_pager && i == cursor_pos) { + if (!cursor_is_within_pager && i == cursor_pos) { cursor_arr = s->desired.cursor; } @@ -1098,7 +1109,7 @@ void s_write(screen_t *s, const wcstring &left_prompt, const wcstring &right_pro // above. s->desired.cursor = cursor_arr; - if (cursor_position_is_within_pager) { + if (cursor_is_within_pager) { s->desired.cursor.x = (int)cursor_pos; s->desired.cursor.y = (int)s->desired.line_count(); } @@ -1173,7 +1184,8 @@ void s_reset(screen_t *s, screen_reset_mode_t mode) { // in common.cpp. int non_space_width = wcwidth(omitted_newline_char); if (screen_width >= non_space_width) { - if (output_get_color_support() & color_support_term256) { + bool has_256_colors = output_get_color_support() & color_support_term256; + if (has_256_colors) { // Draw the string in term256 gray. abandon_line_string.append(L"\x1b[38;5;245m"); } else { diff --git a/src/screen.h b/src/screen.h index 1a26ed130..43e256d15 100644 --- a/src/screen.h +++ b/src/screen.h @@ -148,14 +148,12 @@ class screen_t { /// \param colors the colors to use for the comand line /// \param indent the indent to use for the command line /// \param cursor_pos where the cursor is -/// \param sel_start_pos where the selections starts (inclusive) -/// \param sel_stop_pos where the selections ends (inclusive) /// \param pager_data any pager data, to append to the screen /// \param position_is_within_pager whether the position is within the pager line (first line) void s_write(screen_t *s, const wcstring &left_prompt, const wcstring &right_prompt, const wcstring &commandline, size_t explicit_len, const highlight_spec_t *colors, - const int *indent, size_t cursor_pos, size_t sel_start_pos, size_t sel_stop_pos, - const page_rendering_t &pager_data, bool position_is_within_pager); + const int *indent, size_t cursor_pos, const page_rendering_t &pager_data, + bool cursor_is_within_pager); /// This function resets the screen buffers internal knowledge about the contents of the screen. Use /// this function when some other function than s_write has written to the screen. From 4481692037aba4ef3af1f69c43a8e1b4e1f531f3 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Thu, 5 May 2016 15:09:31 -0700 Subject: [PATCH 227/363] lint: low hanging fruit in history.cpp --- src/history.cpp | 558 +++++++++++++++++++++++------------------------- src/history.h | 12 +- 2 files changed, 274 insertions(+), 296 deletions(-) diff --git a/src/history.cpp b/src/history.cpp index 6f38eb016..c4fb1f30b 100644 --- a/src/history.cpp +++ b/src/history.cpp @@ -44,9 +44,6 @@ // When we rewrite the history, the number of items we keep. #define HISTORY_SAVE_MAX (1024 * 256) -// Whether we print timing information. -#define LOG_TIMES 0 - // Default buffer size for flushing to the history file. #define HISTORY_OUTPUT_BUFFER_SIZE (16 * 1024) @@ -55,15 +52,13 @@ namespace { /// Helper class for certain output. This is basically a string that allows us to ensure we only /// flush at record boundaries, and avoids the copying of ostringstream. Have you ever tried to /// implement your own streambuf? Total insanity. +static size_t safe_strlen(const char *s) { return s ? strlen(s) : 0; } class history_output_buffer_t { // A null-terminated C string. std::vector buffer; - // Offset is the offset of the null terminator. size_t offset; - static size_t safe_strlen(const char *s) { return s ? strlen(s) : 0; } - public: /// Add a bit more to HISTORY_OUTPUT_BUFFER_SIZE because we flush once we've exceeded that size. history_output_buffer_t() : buffer(HISTORY_OUTPUT_BUFFER_SIZE + 128, '\0'), offset(0) {} @@ -113,17 +108,13 @@ class time_profiler_t { public: explicit time_profiler_t(const char *w) { - if (LOG_TIMES) { - what = w; - start = timef(); - } + what = w; + start = timef(); } ~time_profiler_t() { - if (LOG_TIMES) { - double end = timef(); - fprintf(stderr, "(LOG_TIMES %s: %02f msec)\n", what, (end - start) * 1000); - } + double end = timef(); + debug(2, "%s: %.0f ms", what, (end - start) * 1000); } }; @@ -165,12 +156,12 @@ class history_lru_cache_t : public lru_cache_t { // 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) { - node->timestamp = std::max(node->timestamp, item.timestamp()); - // What to do about paths here? Let's just ignore them. - } else { + if (node == NULL) { node = new history_lru_node_t(item); this->add_node(node); + } else { + node->timestamp = std::max(node->timestamp, item.timestamp()); + // What to do about paths here? Let's just ignore them. } } }; @@ -205,6 +196,219 @@ static void escape_yaml(std::string *str); /// Inverse of escape_yaml. static void unescape_yaml(std::string *str); +/// 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) { + // Locate the newline. + assert(cursor <= len); + const char *start = base + cursor; + const char *newline = (char *)memchr(start, '\n', len - cursor); + 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; + } + + // We ran off the end. + result.clear(); + return len - cursor; +} + +/// Trims leading spaces in the given string, returning how many there were. +static size_t trim_leading_spaces(std::string &str) { + size_t i = 0, max = str.size(); + while (i < max && str[i] == ' ') i++; + str.erase(0, i); + return i; +} + +static bool extract_prefix_and_unescape_yaml(std::string *key, std::string *value, + const std::string &line) { + size_t where = line.find(":"); + if (where != std::string::npos) { + key->assign(line, 0, where); + + // Skip a space after the : if necessary. + size_t val_start = where + 1; + if (val_start < line.size() && line.at(val_start) == ' ') val_start++; + value->assign(line, val_start, line.size() - val_start); + + unescape_yaml(key); + unescape_yaml(value); + } + return where != std::string::npos; +} + +/// 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) { + wcstring out; + for (const wchar_t *in = in_str.c_str(); *in; in++) { + if (*in == L'\\') { + if (*(in + 1) != L'\n') { + out.push_back(*in); + } + } else { + out.push_back(*in); + } + } + return out; +} + +/// 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) { + history_file_type_t result = history_type_unknown; + if (len > 0) { // old fish started with a # + if (data[0] == '#') { + result = history_type_fish_1_x; + } else { // assume new fish + result = history_type_fish_2_0; + } + } + return result; +} + +/// Decode an item via the fish 1.x format. Adapted from fish 1.x's item_get(). +static history_item_t decode_item_fish_1_x(const char *begin, size_t length) { + const char *end = begin + length; + const char *pos = begin; + wcstring out; + bool was_backslash = false; + bool first_char = true; + bool timestamp_mode = false; + time_t timestamp = 0; + + while (1) { + wchar_t c; + size_t res; + mbstate_t state = {}; + + if (MB_CUR_MAX == 1) { // single-byte locale + c = (unsigned char)*pos; + res = 1; + } else { + res = mbrtowc(&c, pos, end - pos, &state); + } + + if (res == (size_t)-1) { + pos++; + continue; + } else if (res == (size_t)-2) { + break; + } else if (res == (size_t)0) { + pos++; + continue; + } + pos += res; + + if (c == L'\n') { + if (timestamp_mode) { + const wchar_t *time_string = out.c_str(); + while (*time_string && !iswdigit(*time_string)) time_string++; + errno = 0; + + if (*time_string) { + time_t tm; + wchar_t *end; + + errno = 0; + tm = (time_t)wcstol(time_string, &end, 10); + + if (tm && !errno && !*end) { + timestamp = tm; + } + } + + out.clear(); + timestamp_mode = false; + continue; + } + if (!was_backslash) break; + } + + if (first_char) { + first_char = false; + if (c == L'#') timestamp_mode = true; + } + + out.push_back(c); + was_backslash = (c == L'\\') && !was_backslash; + } + + out = history_unescape_newlines_fish_1_x(out); + return history_item_t(out, timestamp); +} + +/// Decode an item via the fish 2.0 format. +static history_item_t decode_item_fish_2_0(const char *base, size_t len) { + wcstring cmd; + time_t when = 0; + path_list_t paths; + + size_t indent = 0, cursor = 0; + std::string key, value, line; + + // Read the "- cmd:" line. + size_t advance = read_line(base, cursor, len, line); + trim_leading_spaces(line); + if (!extract_prefix_and_unescape_yaml(&key, &value, line) || key != "- cmd") { + goto done; //!OCLINT(goto is the cleanest way to handle bad input) + } + + cursor += advance; + cmd = str2wcstring(value); + + // Read the remaining lines. + for (;;) { + size_t advance = read_line(base, cursor, len, line); + + size_t this_indent = trim_leading_spaces(line); + if (indent == 0) indent = this_indent; + + if (this_indent == 0 || indent != this_indent) break; + + if (!extract_prefix_and_unescape_yaml(&key, &value, line)) break; + + // We are definitely going to consume this line. + cursor += advance; + + if (key == "when") { + // Parse an int from the timestamp. Should this fail, strtol returns 0; that's + // acceptable. + char *end = NULL; + long tmp = strtol(value.c_str(), &end, 0); + when = tmp; + } else if (key == "paths") { + // Read lines starting with " - " until we can't read any more. + for (;;) { + size_t advance = read_line(base, cursor, len, line); + if (trim_leading_spaces(line) <= indent) break; + + if (strncmp(line.c_str(), "- ", 2)) break; + + // We're going to consume this line. + cursor += advance; + + // Skip the leading dash-space and then store this path it. + line.erase(0, 2); + unescape_yaml(&line); + paths.push_back(str2wcstring(line)); + } + } + } + +done: + history_item_t result(cmd, when); + result.set_required_paths(paths); + return result; +} + +static history_item_t decode_item(const char *base, size_t len, history_file_type_t type) { + if (type == history_type_fish_2_0) return decode_item_fish_2_0(base, len); + if (type == history_type_fish_1_x) return decode_item_fish_1_x(base, len); + return history_item_t(L""); +} + /// We can merge two items if they are the same command. We use the more recent timestamp, more /// recent identifier, and the longer list of required paths. bool history_item_t::merge(const history_item_t &item) { @@ -328,11 +532,11 @@ static const char *next_line(const char *start, size_t length) { /// Pass the address and length of a mapped region. /// Pass a pointer to a cursor size_t, initially 0. /// If custoff_timestamp is nonzero, skip items created at or after that timestamp. -/// Returns (size_t)(-1) when done. +/// Returns (size_t)-1 when done. static size_t offset_of_next_item_fish_2_0(const char *begin, size_t mmap_length, size_t *inout_cursor, time_t cutoff_timestamp) { size_t cursor = *inout_cursor; - size_t result = (size_t)(-1); + size_t result = (size_t)-1; while (cursor < mmap_length) { const char *line_start = begin + cursor; @@ -410,8 +614,9 @@ static size_t offset_of_next_item_fish_2_0(const char *begin, size_t mmap_length // We made it through the gauntlet. result = line_start - begin; - break; + break; //!OCLINT(avoid branching statement as last in loop) } + *inout_cursor = cursor; return result; } @@ -419,37 +624,30 @@ 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) { - if (mmap_length == 0 || *inout_cursor >= mmap_length) return (size_t)(-1); + size_t *inout_cursor) { + if (mmap_length == 0 || *inout_cursor >= mmap_length) return (size_t)-1; const char *end = begin + mmap_length; const char *pos; - bool ignore_newline = false; bool do_push = true; bool all_done = false; - size_t result = *inout_cursor; + for (pos = begin + *inout_cursor; pos < end && !all_done; pos++) { if (do_push) { ignore_newline = (*pos == '#'); do_push = false; } - switch (*pos) { - case '\\': { - pos++; - break; - } - case '\n': { - if (!ignore_newline) { - // Note: pos will be left pointing just after this newline, because of the ++ in - // the loop. - all_done = true; - } - ignore_newline = false; - break; + if (*pos == '\\') { + pos++; + } else if (*pos == '\n') { + if (!ignore_newline) { + // pos will be left pointing just after this newline, because of the ++ in the loop. + all_done = true; } + ignore_newline = false; } } @@ -470,13 +668,12 @@ static size_t offset_of_next_item(const char *begin, size_t mmap_length, } case history_type_fish_1_x: { result = - offset_of_next_item_fish_1_x(begin, mmap_length, inout_cursor, cutoff_timestamp); + offset_of_next_item_fish_1_x(begin, mmap_length, inout_cursor); break; } - case history_type_unknown: - default: { + case history_type_unknown: { // Oh well. - result = (size_t)(-1); + result = (size_t)-1; break; } } @@ -486,7 +683,7 @@ static size_t offset_of_next_item(const char *begin, size_t mmap_length, history_t &history_collection_t::alloc(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(m_lock); + scoped_lock locker(m_lock); //!OCLINT(side-effect) history_t *¤t = m_histories[name]; if (current == NULL) current = new history_t(name); return *current; @@ -513,7 +710,7 @@ history_t::history_t(const wcstring &pname) history_t::~history_t() { pthread_mutex_destroy(&lock); } void history_t::add(const history_item_t &item, bool pending) { - scoped_lock locker(lock); + scoped_lock locker(lock); //!OCLINT(side-effect) // Try merging with the last item. if (!new_items.empty() && new_items.back().merge(item)) { @@ -556,7 +753,7 @@ void history_t::save_internal_unless_disabled() { } // This might be a good candidate for moving to a background thread. - time_profiler_t profiler(vacuum ? "save_internal vacuum" : "save_internal no vacuum"); + time_profiler_t profiler(vacuum ? "save_internal vacuum" : "save_internal no vacuum"); //!OCLINT(side-effect) this->save_internal(vacuum); // Update our countdown. @@ -605,7 +802,7 @@ void history_t::set_valid_file_paths(const wcstring_list_t &valid_file_paths, return; } - scoped_lock locker(lock); + scoped_lock locker(lock); //!OCLINT(side-effect) // Look for an item with the given identifier. It is likely to be at the end of new_items. for (history_item_list_t::reverse_iterator iter = new_items.rbegin(); iter != new_items.rend(); @@ -618,7 +815,7 @@ void history_t::set_valid_file_paths(const wcstring_list_t &valid_file_paths, } void history_t::get_string_representation(wcstring *result, const wcstring &separator) { - scoped_lock locker(lock); + scoped_lock locker(lock); //!OCLINT(side-effect) bool first = true; @@ -652,7 +849,7 @@ void history_t::get_string_representation(wcstring *result, const wcstring &sepa 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); + decode_item(mmap_start + offset, mmap_length - offset, mmap_type); // Skip duplicates. if (!seen.insert(item.str()).second) continue; @@ -664,7 +861,7 @@ void history_t::get_string_representation(wcstring *result, const wcstring &sepa } history_item_t history_t::item_at_index(size_t idx) { - scoped_lock locker(lock); + scoped_lock locker(lock); //!OCLINT(side-effect) // 0 is considered an invalid index. assert(idx > 0); @@ -689,233 +886,13 @@ history_item_t history_t::item_at_index(size_t idx) { 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); + return decode_item(mmap_start + offset, mmap_length - offset, mmap_type); } // Index past the valid range, so return an empty history item. return history_item_t(wcstring(), 0); } -/// 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) { - // Locate the newline. - assert(cursor <= len); - const char *start = base + cursor; - const char *newline = (char *)memchr(start, '\n', len - cursor); - 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; - } - - // We ran off the end. - result.clear(); - return len - cursor; -} - -/// Trims leading spaces in the given string, returning how many there were. -static size_t trim_leading_spaces(std::string &str) { - size_t i = 0, max = str.size(); - while (i < max && str[i] == ' ') i++; - str.erase(0, i); - return i; -} - -static bool extract_prefix_and_unescape_yaml(std::string *key, std::string *value, - const std::string &line) { - size_t where = line.find(":"); - if (where != std::string::npos) { - key->assign(line, 0, where); - - // Skip a space after the : if necessary. - size_t val_start = where + 1; - if (val_start < line.size() && line.at(val_start) == ' ') val_start++; - value->assign(line, val_start, line.size() - val_start); - - unescape_yaml(key); - unescape_yaml(value); - } - return where != std::string::npos; -} - -/// 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) { - wcstring cmd; - time_t when = 0; - path_list_t paths; - - size_t indent = 0, cursor = 0; - std::string key, value, line; - - // Read the "- cmd:" line. - size_t advance = read_line(base, cursor, len, line); - trim_leading_spaces(line); - if (!extract_prefix_and_unescape_yaml(&key, &value, line) || key != "- cmd") { - goto done; - } - - cursor += advance; - cmd = str2wcstring(value); - - // Read the remaining lines. - for (;;) { - size_t advance = read_line(base, cursor, len, line); - - size_t this_indent = trim_leading_spaces(line); - if (indent == 0) indent = this_indent; - - if (this_indent == 0 || indent != this_indent) break; - - if (!extract_prefix_and_unescape_yaml(&key, &value, line)) break; - - // We are definitely going to consume this line. - cursor += advance; - - if (key == "when") { - // Parse an int from the timestamp. Should this fail, strtol returns 0; that's - // acceptable. - char *end = NULL; - long tmp = strtol(value.c_str(), &end, 0); - when = tmp; - } else if (key == "paths") { - // Read lines starting with " - " until we can't read any more. - for (;;) { - size_t advance = read_line(base, cursor, len, line); - if (trim_leading_spaces(line) <= indent) break; - - if (strncmp(line.c_str(), "- ", 2)) break; - - // We're going to consume this line. - cursor += advance; - - // Skip the leading dash-space and then store this path it. - line.erase(0, 2); - unescape_yaml(&line); - paths.push_back(str2wcstring(line)); - } - } - } -done: - history_item_t result(cmd, when); - result.required_paths.swap(paths); - return result; -} - -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""); } - } -} - -/// 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) { - wcstring out; - for (const wchar_t *in = in_str.c_str(); *in; in++) { - if (*in == L'\\') { - if (*(in + 1) != L'\n') { - out.push_back(*in); - } - } else { - out.push_back(*in); - } - } - return out; -} - -/// 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) { - const char *end = begin + length; - const char *pos = begin; - wcstring out; - bool was_backslash = false; - bool first_char = true; - bool timestamp_mode = false; - time_t timestamp = 0; - - while (1) { - wchar_t c; - size_t res; - mbstate_t state = {}; - - if (MB_CUR_MAX == 1) { // single-byte locale - c = (unsigned char)*pos; - res = 1; - } else { - res = mbrtowc(&c, pos, end - pos, &state); - } - - if (res == (size_t)-1) { - pos++; - continue; - } else if (res == (size_t)-2) { - break; - } else if (res == (size_t)0) { - pos++; - continue; - } - pos += res; - - if (c == L'\n') { - if (timestamp_mode) { - const wchar_t *time_string = out.c_str(); - while (*time_string && !iswdigit(*time_string)) time_string++; - errno = 0; - - if (*time_string) { - time_t tm; - wchar_t *end; - - errno = 0; - tm = (time_t)wcstol(time_string, &end, 10); - - if (tm && !errno && !*end) { - timestamp = tm; - } - } - - out.clear(); - timestamp_mode = false; - continue; - } - if (!was_backslash) break; - } - - if (first_char) { - if (c == L'#') timestamp_mode = true; - } - - first_char = false; - - out.push_back(c); - - was_backslash = ((c == L'\\') && !was_backslash); - } - - out = history_unescape_newlines_fish_1_x(out); - return history_item_t(out, timestamp); -} - -/// 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) { - history_file_type_t result = history_type_unknown; - if (len > 0) { // old fish started with a # - if (data[0] == '#') { - result = history_type_fish_1_x; - } else { // assume new fish - result = history_type_fish_2_0; - } - } - return result; -} - void history_t::populate_from_mmap(void) { mmap_type = infer_file_type(mmap_start, mmap_length); size_t cursor = 0; @@ -923,7 +900,7 @@ void history_t::populate_from_mmap(void) { size_t offset = offset_of_next_item(mmap_start, mmap_length, mmap_type, &cursor, boundary_timestamp); // If we get back -1, we're done. - if (offset == (size_t)(-1)) break; + if (offset == (size_t)-1) break; // Remember this item. old_item_offsets.push_back(offset); @@ -985,7 +962,7 @@ bool history_t::load_old_if_needed(void) { if (map_file(name, &mmap_start, &mmap_length, &mmap_file_id)) { // Here we've mapped the file. ok = true; - time_profiler_t profiler("populate_from_mmap"); + time_profiler_t profiler("populate_from_mmap"); //!OCLINT(side-effect) this->populate_from_mmap(); } @@ -1013,7 +990,7 @@ bool history_search_t::go_forwards() { bool history_search_t::go_backwards() { // Backwards means increasing our index. - const size_t max_idx = (size_t)(-1); + const size_t max_idx = (size_t)-1; size_t idx = 0; if (!prev_matches.empty()) idx = prev_matches.back().first; @@ -1053,12 +1030,13 @@ 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) { // Go backwards as far as we can. - while (go_backwards()) - ; // noop + while (go_backwards()) { //!OCLINT(empty while statement) + // Do nothing. + } } history_item_t history_search_t::current_item() const { - assert(!prev_matches.empty()); + assert(!prev_matches.empty()); //!OCLINT(double negative) return prev_matches.back().second; } @@ -1192,10 +1170,10 @@ bool history_t::save_internal_via_rewrite() { 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)) break; + if (offset == (size_t)-1) break; // Try decoding an old item. - const history_item_t old_item = history_t::decode_item( + const history_item_t old_item = decode_item( local_mmap_start + offset, local_mmap_size - offset, local_mmap_type); if (old_item.empty() || deleted_items.count(old_item.str()) > 0) { // debug(0, L"Item is deleted : %s\n", @@ -1423,25 +1401,25 @@ void history_t::save_internal(bool vacuum) { } void history_t::save(void) { - scoped_lock locker(lock); + scoped_lock locker(lock); //!OCLINT(side-effect) this->save_internal(false); } void history_t::disable_automatic_saving() { - scoped_lock locker(lock); + scoped_lock locker(lock); //!OCLINT(side-effect) disable_automatic_save_counter++; assert(disable_automatic_save_counter != 0); // overflow! } void history_t::enable_automatic_saving() { - scoped_lock locker(lock); + scoped_lock locker(lock); //!OCLINT(side-effect) assert(disable_automatic_save_counter > 0); // underflow disable_automatic_save_counter--; save_internal_unless_disabled(); } void history_t::clear(void) { - scoped_lock locker(lock); + scoped_lock locker(lock); //!OCLINT(side-effect) new_items.clear(); deleted_items.clear(); first_unwritten_new_item_index = 0; @@ -1452,7 +1430,7 @@ void history_t::clear(void) { } bool history_t::is_empty(void) { - scoped_lock locker(lock); + scoped_lock locker(lock); //!OCLINT(side-effect) // If we have new items, we're not empty. if (!new_items.empty()) return false; @@ -1538,12 +1516,13 @@ void history_t::populate_from_bash(FILE *stream) { std::string line; for (;;) { line.clear(); - bool success = false, has_newline = false; + bool success = false; + bool has_newline = false; // Loop until we've read a line. do { char buff[128]; - success = !!fgets(buff, sizeof buff, stream); + success = (bool)fgets(buff, sizeof buff, stream); if (success) { // Skip the newline. char *newline = strchr(buff, '\n'); @@ -1571,7 +1550,7 @@ void history_t::incorporate_external_changes() { // to preserve old_item_offsets so that they don't have to be recomputed. (However, then items // *deleted* in other instances would not show up here). time_t new_timestamp = time(NULL); - scoped_lock locker(lock); + scoped_lock locker(lock); //!OCLINT(side-effect) // If for some reason the clock went backwards, we don't want to start dropping items; therefore // we only do work if time has progressed. This also makes multiple calls cheap. @@ -1601,6 +1580,9 @@ void history_sanity_check() { int file_detection_context_t::perform_file_detection(bool test_all) { ASSERT_IS_BACKGROUND_THREAD(); valid_paths.clear(); + // TODO: Figure out why this bothers to return a variable result since the only consumer, + // perform_file_detection_done(), ignores the result. It seems like either this should always + // return a constant or perform_file_detection_done() should use our return value. int result = 1; for (path_list_t::const_iterator iter = potential_paths.begin(); iter != potential_paths.end(); ++iter) { @@ -1630,7 +1612,7 @@ static int threaded_perform_file_detection(file_detection_context_t *ctx) { 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) { //!OCLINT(success is ignored) ASSERT_IS_MAIN_THREAD(); // Now that file detection is done, update the history item with the valid file paths. @@ -1721,6 +1703,6 @@ void history_t::add_pending_with_file_detection(const wcstring &str) { /// Very simple, just mark that we have no more pending items. void history_t::resolve_pending() { - scoped_lock locker(lock); + scoped_lock locker(lock); //!OCLINT(side-effect) this->has_pending_item = false; } diff --git a/src/history.h b/src/history.h index 0aa09b7c1..114df9571 100644 --- a/src/history.h +++ b/src/history.h @@ -52,9 +52,6 @@ class history_item_t { friend class history_tests_t; private: - explicit history_item_t(const wcstring &str); - explicit history_item_t(const wcstring &, time_t, history_identifier_t ident = 0); - // Attempts to merge two compatible history items together. bool merge(const history_item_t &item); @@ -71,6 +68,9 @@ class history_item_t { path_list_t required_paths; public: + explicit history_item_t(const wcstring &str); + explicit history_item_t(const wcstring &, time_t, history_identifier_t ident = 0); + const wcstring &str() const { return contents; } bool empty() const { return contents.empty(); } @@ -81,6 +81,7 @@ class history_item_t { time_t timestamp() const { return creation_timestamp; } const path_list_t &get_required_paths() const { return required_paths; } + void set_required_paths(path_list_t paths) { required_paths = paths; } bool operator==(const history_item_t &other) const { return contents == other.contents && creation_timestamp == other.creation_timestamp && @@ -191,11 +192,6 @@ class history_t { // Whether we're in maximum chaos mode, useful for testing. bool chaos_mode; - // Versioned decoding. - static history_item_t decode_item_fish_2_0(const char *base, size_t len); - static history_item_t decode_item_fish_1_x(const char *base, size_t len); - static history_item_t decode_item(const char *base, size_t len, history_file_type_t type); - public: explicit history_t(const wcstring &); // constructor ~history_t(); // desctructor From bc6cc4c10566f140c6c0575da8153c58fd8772c4 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Thu, 5 May 2016 20:05:45 -0700 Subject: [PATCH 228/363] fix fork debug printf() calls The fork (create new process) related debugging messages rely on an undocumented env var and use `printf()` rather than `debug()`. There are also errors in how the fork count is tracked that this fixes. Fixes #2995 --- src/env.cpp | 5 ---- src/env.h | 2 -- src/exec.cpp | 65 ++++++++++++++++++++-------------------------------- src/fish.cpp | 5 ---- 4 files changed, 25 insertions(+), 52 deletions(-) diff --git a/src/env.cpp b/src/env.cpp index 6d5e2ab87..12eef036b 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -46,7 +46,6 @@ /// At init, we read all the environment variables from this array. extern char **environ; -bool g_log_forks = false; bool g_use_posix_spawn = false; // will usually be set to true /// Struct representing one level in the function variable stack. @@ -392,10 +391,6 @@ void env_init(const struct config_paths_t *paths /* or NULL */) { s_universal_variables = new env_universal_t(L""); s_universal_variables->load(); - // Set g_log_forks. - env_var_t log_forks = env_get_string(L"fish_log_forks"); - g_log_forks = !log_forks.missing_or_empty() && from_string(log_forks); - // Set g_use_posix_spawn. Default to true. env_var_t use_posix_spawn = env_get_string(L"fish_use_posix_spawn"); g_use_posix_spawn = diff --git a/src/env.h b/src/env.h index 682cf88d3..e758a594b 100644 --- a/src/env.h +++ b/src/env.h @@ -193,9 +193,7 @@ class env_vars_snapshot_t { static const wchar_t *const completing_keys[]; }; -extern bool g_log_forks; extern int g_fork_count; - extern bool g_use_posix_spawn; /// A variable entry. Stores the value of a variable and whether it should be exported. diff --git a/src/exec.cpp b/src/exec.cpp index 79f461d19..24a9af05c 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -485,19 +485,17 @@ void exec_job(parser_t &parser, job_t *j) { if (needs_keepalive) { // Call fork. No need to wait for threads since our use is confined and simple. - if (g_log_forks) { - printf("fork #%d: Executing keepalive fork for '%ls'\n", g_fork_count, - j->command_wcstr()); - } keepalive.pid = execute_fork(false); if (keepalive.pid == 0) { - /* Child */ + // Child keepalive.pid = getpid(); set_child_group(j, &keepalive, 1); pause(); exit_without_destructors(0); } else { - /* Parent */ + // Parent + debug(2, L"Fork #%d, pid %d: keepalive fork for '%ls'", g_fork_count, keepalive.pid, + j->command_wcstr()); set_child_group(j, &keepalive, 0); } } @@ -870,10 +868,6 @@ void exec_job(parser_t &parser, job_t *j) { if (block_output_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. @@ -884,6 +878,8 @@ void exec_job(parser_t &parser, job_t *j) { } else { // This is the parent process. Store away information on the child, and // possibly give it control over the terminal. + debug(2, L"Fork #%d, pid %d: internal block or function for '%ls'", + g_fork_count, pid, p->argv0()); p->pid = pid; set_child_group(j, p, 0); } @@ -929,23 +925,15 @@ void exec_job(parser_t &parser, job_t *j) { if (no_stdout_output && no_stderr_output) { // The builtin produced no output and is not inside of a pipeline. No // need to fork or even output anything. - if (g_log_forks) { - // This one is very wordy. - // printf("fork #-: Skipping fork due to no output for internal - // builtin for '%ls'\n", p->argv0()); - } + debug(3, L"Skipping fork: no output for internal builtin '%ls'", + p->argv0()); fork_was_skipped = true; } else if (no_stderr_output && stdout_is_to_buffer) { // The builtin produced no stderr, and its stdout is going to an // internal buffer. There is no need to fork. This helps out the // performance quite a bit in complex completion code. - if (g_log_forks) { - printf( - "fork #-: Skipping fork due to buffered output for internal " - "builtin for '%ls'\n", - p->argv0()); - } - + debug(3, L"Skipping fork: buffered output for internal builtin '%ls'", + p->argv0()); CAST_INIT(io_buffer_t *, io_buffer, stdout_io.get()); const std::string res = wcs2string(builtin_io_streams->out.buffer()); io_buffer->out_buffer_append(res.data(), res.size()); @@ -953,12 +941,8 @@ void exec_job(parser_t &parser, job_t *j) { } else if (stdout_io.get() == NULL && stderr_io.get() == NULL) { // We are writing to normal stdout and stderr. Just do it - no need to // fork. - if (g_log_forks) { - printf( - "fork #-: Skipping fork due to ordinary output for internal " - "builtin for '%ls'\n", - p->argv0()); - } + debug(3, L"Skipping fork: ordinary output for internal builtin '%ls'", + p->argv0()); const std::string outbuff = wcs2string(stdout_buffer); const std::string errbuff = wcs2string(stderr_buffer); bool builtin_io_done = do_builtin_io(outbuff.data(), outbuff.size(), @@ -997,10 +981,6 @@ void exec_job(parser_t &parser, job_t *j) { fflush(stdout); fflush(stderr); - if (g_log_forks) { - printf("fork #%d: Executing fork for internal builtin for '%ls'\n", - g_fork_count, p->argv0()); - } pid = execute_fork(false); if (pid == 0) { // This is the child process. Setup redirections, print correct output to @@ -1012,6 +992,8 @@ void exec_job(parser_t &parser, job_t *j) { } else { // This is the parent process. Store away information on the child, and // possibly give it control over the terminal. + debug(2, L"Fork #%d, pid %d: internal builtin for '%ls'", g_fork_count, pid, + p->argv0()); p->pid = pid; set_child_group(j, p, 0); @@ -1040,16 +1022,13 @@ void exec_job(parser_t &parser, job_t *j) { const char *actual_cmd = actual_cmd_str.c_str(); const wchar_t *reader_current_filename(void); - if (g_log_forks) { - const wchar_t *file = reader_current_filename(); - printf("fork #%d: forking for '%s' in '%ls'\n", g_fork_count, actual_cmd, - file ? file : L""); - } + const wchar_t *file = reader_current_filename(); #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) { + g_fork_count++; // spawn counts as a fork+exec // Create posix spawn attributes and actions. posix_spawnattr_t attr = posix_spawnattr_t(); posix_spawn_file_actions_t actions = posix_spawn_file_actions_t(); @@ -1079,6 +1058,8 @@ void exec_job(parser_t &parser, job_t *j) { // 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. + debug(2, L"Fork #%d, pid %d: spawn external command '%s' from '%ls'\n", + g_fork_count, pid, actual_cmd, file ? file : L""); if (pid == 0) { job_mark_process_as_failed(j, p); exec_error = true; @@ -1094,9 +1075,13 @@ void exec_job(parser_t &parser, job_t *j) { safe_launch_process(p, actual_cmd, argv, envv); // safe_launch_process _never_ returns... assert(0 && "safe_launch_process should not have returned"); - } else if (pid < 0) { - job_mark_process_as_failed(j, p); - exec_error = true; + } else { + debug(2, L"Fork #%d, pid %d: external command '%s' from '%ls'\n", + g_fork_count, pid, p->argv0(), file ? file : L""); + if (pid < 0) { + job_mark_process_as_failed(j, p); + exec_error = true; + } } } diff --git a/src/fish.cpp b/src/fish.cpp index dcad6c8b3..d8192c86d 100644 --- a/src/fish.cpp +++ b/src/fish.cpp @@ -440,8 +440,6 @@ int main(int argc, char **argv) { parser_t &parser = parser_t::principal_parser(); - if (g_log_forks) printf("%d: g_fork_count: %d\n", __LINE__, g_fork_count); - const io_chain_t empty_ios; if (read_init(paths)) { // Stomp the exit status of any initialization commands (issue #635). @@ -515,9 +513,6 @@ int main(int argc, char **argv) { builtin_destroy(); reader_destroy(); event_destroy(); - - if (g_log_forks) printf("%d: g_fork_count: %d\n", __LINE__, g_fork_count); - exit_without_destructors(exit_status); return EXIT_FAILURE; // above line should always exit } From 1c6f6df2b3ae4a13d89242964ca3dbd3c146d1fe Mon Sep 17 00:00:00 2001 From: Elis Axelsson Date: Fri, 6 May 2016 16:15:47 +0200 Subject: [PATCH 229/363] Change abbr to allow non-letter keys (#2996) --- share/functions/abbr.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/functions/abbr.fish b/share/functions/abbr.fish index bb50ec023..8606f84b2 100644 --- a/share/functions/abbr.fish +++ b/share/functions/abbr.fish @@ -85,7 +85,7 @@ function abbr --description "Manage abbreviations" set key $kv[1] set value $kv[2] # ensure the key contains at least one non-space character - if not string match -qr "\w" -- $key + if not string match -qr "[^\s]" -- $key printf ( _ "%s: abbreviation must have a non-empty key\n" ) abbr >&2 return 1 end From 8ec81393a36dea2a76dd85a3858a2835b5c32ff6 Mon Sep 17 00:00:00 2001 From: Elis Axelsson Date: Fri, 6 May 2016 16:15:47 +0200 Subject: [PATCH 230/363] Change abbr to allow non-letter keys (#2996) (cherry picked from commit 1c6f6df2b3ae4a13d89242964ca3dbd3c146d1fe) --- share/functions/abbr.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/functions/abbr.fish b/share/functions/abbr.fish index bb50ec023..8606f84b2 100644 --- a/share/functions/abbr.fish +++ b/share/functions/abbr.fish @@ -85,7 +85,7 @@ function abbr --description "Manage abbreviations" set key $kv[1] set value $kv[2] # ensure the key contains at least one non-space character - if not string match -qr "\w" -- $key + if not string match -qr "[^\s]" -- $key printf ( _ "%s: abbreviation must have a non-empty key\n" ) abbr >&2 return 1 end From 01e5ca5c9679b8745f660469afc1995c3c44f3fd Mon Sep 17 00:00:00 2001 From: Alexey Alekhin Date: Sat, 7 May 2016 02:05:37 +0200 Subject: [PATCH 231/363] Changed the code to add fish to /etc/shells to the one that is mentioned in the Readme --- doc_src/faq.hdr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc_src/faq.hdr b/doc_src/faq.hdr index abbedb8b1..bc549ee57 100644 --- a/doc_src/faq.hdr +++ b/doc_src/faq.hdr @@ -136,10 +136,10 @@ The `open` command uses the MIME type database and the `.desktop` files used by


    \section faq-default How do I make fish my default shell? -If you installed fish manually (e.g. by compiling it, not by using a package manager), you first need to add fish to the list of shells by executing the following command (assuming you installed fish in /usr/local) as root: +If you installed fish manually (e.g. by compiling it, not by using a package manager), you first need to add fish to the list of shells by executing the following command (assuming you installed fish in /usr/local): \fish{cli-dark} -echo /usr/local/bin/fish >>/etc/shells +echo /usr/local/bin/fish | sudo tee -a /etc/shells \endfish If you installed a prepackaged version of fish, the package manager should have already done this for you. From c1e15ef084016939ddbe645e108f75416d5b32e0 Mon Sep 17 00:00:00 2001 From: Alexey Alekhin Date: Sat, 7 May 2016 02:05:37 +0200 Subject: [PATCH 232/363] FAQ: Synchronise code to add fish to /etc/shells with README (cherry picked from commit 01e5ca5c9679b8745f660469afc1995c3c44f3fd) Signed-off-by: David Adam --- doc_src/faq.hdr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc_src/faq.hdr b/doc_src/faq.hdr index abbedb8b1..bc549ee57 100644 --- a/doc_src/faq.hdr +++ b/doc_src/faq.hdr @@ -136,10 +136,10 @@ The `open` command uses the MIME type database and the `.desktop` files used by
    \section faq-default How do I make fish my default shell? -If you installed fish manually (e.g. by compiling it, not by using a package manager), you first need to add fish to the list of shells by executing the following command (assuming you installed fish in /usr/local) as root: +If you installed fish manually (e.g. by compiling it, not by using a package manager), you first need to add fish to the list of shells by executing the following command (assuming you installed fish in /usr/local): \fish{cli-dark} -echo /usr/local/bin/fish >>/etc/shells +echo /usr/local/bin/fish | sudo tee -a /etc/shells \endfish If you installed a prepackaged version of fish, the package manager should have already done this for you. From ac47100a7d89d53fa2c44c7de692617741387f38 Mon Sep 17 00:00:00 2001 From: Daniel Bergmann Date: Sat, 7 May 2016 03:05:44 -0700 Subject: [PATCH 233/363] Add tags to completion list for git show. (#2998) --- share/completions/git.fish | 1 + 1 file changed, 1 insertion(+) diff --git a/share/completions/git.fish b/share/completions/git.fish index d47d059a8..d8e7b1a83 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -245,6 +245,7 @@ complete -f -c git -n "__fish_git_using_command remote; and __fish_seen_subcomma complete -f -c git -n '__fish_git_needs_command' -a show -d 'Shows the last commit of a branch' complete -f -c git -n '__fish_git_using_command show' -a '(__fish_git_branches)' -d 'Branch' complete -f -c git -n '__fish_git_using_command show' -a '(__fish_git_unique_remote_branches)' -d 'Remote branch' +complete -f -c git -n '__fish_git_using_command show' -a '(__fish_git_tags)' --description 'Tag' complete -f -c git -n '__fish_git_using_command show' -a '(__fish_git_commits)' # TODO options From 1d101ef3d01a7a8f7e651527fc81ee4bd4588ac5 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 7 May 2016 19:48:01 +0200 Subject: [PATCH 234/363] docs: Mention cartesian product in variable-expansion section See #3002. --- doc_src/index.hdr.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index 24f70e318..5e4d77240 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -522,7 +522,7 @@ echo The plural of $WORD is {$WORD}s Note that without the quotes or braces, fish will try to expand a variable called `$WORDs`, which may not exist. -The latter syntax `{$WORD}` works by exploiting brace expansion; care should be taken with array variables and undefined variables, as these behave very differently to POSIX shells. +The latter syntax `{$WORD}` works by exploiting brace expansion; care should be taken with array variables and undefined variables, as these expand as a cartesian product, so undefined variables eliminate the string. Variable expansion is the only type of expansion performed on double quoted strings. There is, however, an important difference in how variables are expanded when quoted and when unquoted. An unquoted variable expansion will result in a variable number of arguments. For example, if the variable `$foo` has zero elements or is undefined, the argument `$foo` will expand to zero elements. If the variable $foo is an array of five elements, the argument `$foo` will expand to five elements. When quoted, like `"$foo"`, a variable expansion will always result in exactly one argument. Undefined variables will expand to the empty string, and array variables will be concatenated using the space character. From 59c8800c4d0c77d8f6490f47b78e9465aaa474e4 Mon Sep 17 00:00:00 2001 From: "[Redacted]" Date: Sun, 8 May 2016 10:01:57 +0200 Subject: [PATCH 235/363] Added completions for rmmod (#3007) --- share/completions/rmmod.fish | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 share/completions/rmmod.fish diff --git a/share/completions/rmmod.fish b/share/completions/rmmod.fish new file mode 100644 index 000000000..3f6dff165 --- /dev/null +++ b/share/completions/rmmod.fish @@ -0,0 +1,8 @@ +# rmmod completion +complete -c rmmod -x -a "(/sbin/lsmod | awk 'NR > 1 {print \$1}')" + +complete -c rmmod -s h -l help -d "Prints the help text." +complete -c rmmod -s s -l syslog -d "Send errors to syslog instead of standard error." +complete -c rmmod -s v -l verbose -d "Print messages about what the program is doing." +complete -c rmmod -s V -l version -d "Show version of program and exit" +complete -c rmmod -s f -l force -d "With this option, you can remove modules which are being used, or which are not designed to be removed, or have been marked as unsafe" From 100eef4e426402ebde1040dbfab8ded5a20854d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20=C4=8Cun=C3=A1t?= Date: Sun, 8 May 2016 13:51:30 +0200 Subject: [PATCH 236/363] docs: fix location of generated_completions (#3010) --- doc_src/index.hdr.in | 2 +- share/tools/create_manpage_completions.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index 5e4d77240..ff0141360 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -396,7 +396,7 @@ By default, Fish searches the following for completions, using the first availab - A directory for systems administrators to install completions for all users on the system, usually `/etc/fish/completions`; - A directory for third-party software vendors to ship their own completions for their software, usually `/usr/share/fish/vendor_completions.d`; - The completions shipped with fish, usually installed in `/usr/share/fish/completions`; and -- Completions automatically generated from the operating system's manual, usually stored in `~/.local/share/generated_completions`. +- Completions automatically generated from the operating system's manual, usually stored in `~/.local/share/fish/generated_completions`. These paths are controlled by parameters set at build, install, or run time, and may vary from the defaults listed above. diff --git a/share/tools/create_manpage_completions.py b/share/tools/create_manpage_completions.py index 471205fef..bd15eaebf 100755 --- a/share/tools/create_manpage_completions.py +++ b/share/tools/create_manpage_completions.py @@ -903,7 +903,7 @@ def usage(script_name): -h, --help\t\tShow this help message -v, --verbose [0, 1, 2]\tShow debugging output to stderr. Larger is more verbose. -s, --stdout\tWrite all completions to stdout (trumps the --directory option) - -d, --directory [dir]\tWrite all completions to the given directory, instead of to ~/.config/fish/generated_completions + -d, --directory [dir]\tWrite all completions to the given directory, instead of to ~/.local/share/fish/generated_completions -m, --manpath\tProcess all man1 and man8 files available in the manpath (as determined by manpath) -p, --progress\tShow progress """) From 4efd0dfd655097d91f871bc06c6e418a6ef2060f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20=C4=8Cun=C3=A1t?= Date: Sun, 8 May 2016 13:51:30 +0200 Subject: [PATCH 237/363] docs: fix location of generated_completions (#3010) (cherry picked from commit 100eef4e426402ebde1040dbfab8ded5a20854d1) --- doc_src/index.hdr.in | 2 +- share/tools/create_manpage_completions.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index 7e889d8e6..8ff60f642 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -396,7 +396,7 @@ By default, Fish searches the following for completions, using the first availab - A directory for systems administrators to install completions for all users on the system, usually `/etc/fish/completions`; - A directory for third-party software vendors to ship their own completions for their software, usually `/usr/share/fish/vendor_completions.d`; - The completions shipped with fish, usually installed in `/usr/share/fish/completions`; and -- Completions automatically generated from the operating system's manual, usually stored in `~/.local/share/generated_completions`. +- Completions automatically generated from the operating system's manual, usually stored in `~/.local/share/fish/generated_completions`. These paths are controlled by parameters set at build, install, or run time, and may vary from the defaults listed above. diff --git a/share/tools/create_manpage_completions.py b/share/tools/create_manpage_completions.py index 471205fef..bd15eaebf 100755 --- a/share/tools/create_manpage_completions.py +++ b/share/tools/create_manpage_completions.py @@ -903,7 +903,7 @@ def usage(script_name): -h, --help\t\tShow this help message -v, --verbose [0, 1, 2]\tShow debugging output to stderr. Larger is more verbose. -s, --stdout\tWrite all completions to stdout (trumps the --directory option) - -d, --directory [dir]\tWrite all completions to the given directory, instead of to ~/.config/fish/generated_completions + -d, --directory [dir]\tWrite all completions to the given directory, instead of to ~/.local/share/fish/generated_completions -m, --manpath\tProcess all man1 and man8 files available in the manpath (as determined by manpath) -p, --progress\tShow progress """) From 4d1e77fcae5eea22a0fc8ced80d1d4a570df4fd5 Mon Sep 17 00:00:00 2001 From: "[Redacted]" Date: Sun, 8 May 2016 10:01:57 +0200 Subject: [PATCH 238/363] Added completions for rmmod (#3007) (cherry picked from commit 59c8800c4d0c77d8f6490f47b78e9465aaa474e4) --- share/completions/rmmod.fish | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 share/completions/rmmod.fish diff --git a/share/completions/rmmod.fish b/share/completions/rmmod.fish new file mode 100644 index 000000000..3f6dff165 --- /dev/null +++ b/share/completions/rmmod.fish @@ -0,0 +1,8 @@ +# rmmod completion +complete -c rmmod -x -a "(/sbin/lsmod | awk 'NR > 1 {print \$1}')" + +complete -c rmmod -s h -l help -d "Prints the help text." +complete -c rmmod -s s -l syslog -d "Send errors to syslog instead of standard error." +complete -c rmmod -s v -l verbose -d "Print messages about what the program is doing." +complete -c rmmod -s V -l version -d "Show version of program and exit" +complete -c rmmod -s f -l force -d "With this option, you can remove modules which are being used, or which are not designed to be removed, or have been marked as unsafe" From 46b819a26502a203e4a6c3d1f296789a98d787e9 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 7 May 2016 19:48:01 +0200 Subject: [PATCH 239/363] docs: Mention cartesian product in variable-expansion section See #3002. (cherry picked from commit 1d101ef3d01a7a8f7e651527fc81ee4bd4588ac5) --- doc_src/index.hdr.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index 8ff60f642..58b13bb6b 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -522,7 +522,7 @@ echo The plural of $WORD is {$WORD}s Note that without the quotes or braces, fish will try to expand a variable called `$WORDs`, which may not exist. -The latter syntax `{$WORD}` works by exploiting brace expansion; care should be taken with array variables and undefined variables, as these behave very differently to POSIX shells. +The latter syntax `{$WORD}` works by exploiting brace expansion; care should be taken with array variables and undefined variables, as these expand as a cartesian product, so undefined variables eliminate the string. Variable expansion is the only type of expansion performed on double quoted strings. There is, however, an important difference in how variables are expanded when quoted and when unquoted. An unquoted variable expansion will result in a variable number of arguments. For example, if the variable `$foo` has zero elements or is undefined, the argument `$foo` will expand to zero elements. If the variable $foo is an array of five elements, the argument `$foo` will expand to five elements. When quoted, like `"$foo"`, a variable expansion will always result in exactly one argument. Undefined variables will expand to the empty string, and array variables will be concatenated using the space character. From c53951b9b3a340de049c6b5417b99f88f4427f47 Mon Sep 17 00:00:00 2001 From: Daniel Bergmann Date: Sat, 7 May 2016 03:05:44 -0700 Subject: [PATCH 240/363] Add tags to completion list for git show. (#2998) (cherry picked from commit ac47100a7d89d53fa2c44c7de692617741387f38) --- share/completions/git.fish | 1 + 1 file changed, 1 insertion(+) diff --git a/share/completions/git.fish b/share/completions/git.fish index e4e0ba36b..1f25d9fd6 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -245,6 +245,7 @@ complete -f -c git -n "__fish_git_using_command remote; and __fish_seen_subcomma complete -f -c git -n '__fish_git_needs_command' -a show -d 'Shows the last commit of a branch' complete -f -c git -n '__fish_git_using_command show' -a '(__fish_git_branches)' -d 'Branch' complete -f -c git -n '__fish_git_using_command show' -a '(__fish_git_unique_remote_branches)' -d 'Remote branch' +complete -f -c git -n '__fish_git_using_command show' -a '(__fish_git_tags)' --description 'Tag' complete -f -c git -n '__fish_git_using_command show' -a '(__fish_git_commits)' # TODO options From 0d1d324e9f6719a5d4198ee0e8efd05cad89fc39 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sun, 8 May 2016 12:08:23 -0700 Subject: [PATCH 241/363] only deal with files that exist I noticed while working on an unrelated change that deleting a file caused `make lint` to behave in an unexpected manner. --- build_tools/lint.fish | 7 +++++-- build_tools/style.fish | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/build_tools/lint.fish b/build_tools/lint.fish index 5ac05d7d2..f40d4c354 100755 --- a/build_tools/lint.fish +++ b/build_tools/lint.fish @@ -54,8 +54,11 @@ else set files (git show --word-diff=porcelain --name-only --pretty=oneline)[2..-1] end - # Extract just the C/C++ files. - set c_files (string match -r '.*\.c(?:pp)?$' -- $files) + # Extract just the C/C++ files that exist. + set c_files + for file in (string match -r '.*\.c(?:pp)?$' -- $files) + test -f $file; and set c_files $c_files $file + end end # We now have a list of files to check so run the linters. diff --git a/build_tools/style.fish b/build_tools/style.fish index ee54216b4..b5b3b8719 100755 --- a/build_tools/style.fish +++ b/build_tools/style.fish @@ -42,8 +42,11 @@ else set files (git show --name-only --pretty=oneline | tail --lines=+2) end - # Extract just the C/C++ files. - set c_files (string match -r '^.*\.(?:c|cpp|h)$' -- $files) + # Extract just the C/C++ files that exist. + set c_files + for file in (string match -r '^.*\.(?:c|cpp|h)$' -- $files) + test -f $file; and set c_files $c_files $file + end # Extract just the fish files. set f_files (string match -r '^.*\.fish$' -- $files) end From 3626c39398db095baaf2929f9c5bb1db8e7d3603 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sun, 8 May 2016 16:27:15 -0700 Subject: [PATCH 242/363] fix the style of several functions I'm going to modify these functions as part of dealing with issue #3000 and don't want those changes to be masked by running the files through `make style`. --- .../functions/__fish_config_interactive.fish | 526 +++++++++--------- share/functions/cd.fish | 5 +- share/functions/history.fish | 249 +++++---- 3 files changed, 403 insertions(+), 377 deletions(-) diff --git a/share/functions/__fish_config_interactive.fish b/share/functions/__fish_config_interactive.fish index f8b08de62..7b306e3d5 100644 --- a/share/functions/__fish_config_interactive.fish +++ b/share/functions/__fish_config_interactive.fish @@ -1,83 +1,106 @@ -# Initializations that should only be performed when entering -# interactive mode. - -# This function is called by the __fish_on_interactive function, which -# is defined in config.fish. - +# +# Initializations that should only be performed when entering interactive mode. +# +# This function is called by the __fish_on_interactive function, which is defined in config.fish. +# function __fish_config_interactive -d "Initializations that should be performed when entering interactive mode" - # Make sure this function is only run once - if set -q __fish_config_interactive_done - return - end + # Make sure this function is only run once + if set -q __fish_config_interactive_done + return + end - set -g __fish_config_interactive_done + set -g __fish_config_interactive_done - # Set the correct configuration directory - set -l configdir ~/.config - if set -q XDG_CONFIG_HOME - set configdir $XDG_CONFIG_HOME - end - # Set the correct user data directory - set -l userdatadir ~/.local/share - if set -q XDG_DATA_HOME - set userdatadir $XDG_DATA_HOME - end + # Set the correct configuration directory + set -l configdir ~/.config + if set -q XDG_CONFIG_HOME + set configdir $XDG_CONFIG_HOME + end + # Set the correct user data directory + set -l userdatadir ~/.local/share + if set -q XDG_DATA_HOME + set userdatadir $XDG_DATA_HOME + end - # - # If we are starting up for the first time, set various defaults - # + # + # If we are starting up for the first time, set various defaults + # - if not set -q __fish_init_1_50_0 - if not set -q fish_greeting - set -l line1 (printf (_ 'Welcome to fish, the friendly interactive shell') ) - set -l line2 (printf (_ 'Type %shelp%s for instructions on how to use fish') (set_color green) (set_color normal)) - set -U fish_greeting $line1\n$line2 - end - set -U __fish_init_1_50_0 + if not set -q __fish_init_1_50_0 + if not set -q fish_greeting + set -l line1 (printf (_ 'Welcome to fish, the friendly interactive shell') ) + set -l line2 (printf (_ 'Type %shelp%s for instructions on how to use fish') (set_color green) (set_color normal)) + set -U fish_greeting $line1\n$line2 + end + set -U __fish_init_1_50_0 - # Regular syntax highlighting colors - set -q fish_color_normal; or set -U fish_color_normal normal - set -q fish_color_command; or set -U fish_color_command 005fd7 purple - set -q fish_color_param; or set -U fish_color_param 00afff cyan - set -q fish_color_redirection; or set -U fish_color_redirection normal - set -q fish_color_comment; or set -U fish_color_comment red - set -q fish_color_error; or set -U fish_color_error red --bold - set -q fish_color_escape; or set -U fish_color_escape cyan - set -q fish_color_operator; or set -U fish_color_operator cyan - set -q fish_color_end; or set -U fish_color_end green - set -q fish_color_quote; or set -U fish_color_quote brown - set -q fish_color_autosuggestion; or set -U fish_color_autosuggestion 555 yellow - set -q fish_color_user; or set -U fish_color_user green + # Regular syntax highlighting colors + set -q fish_color_normal + or set -U fish_color_normal normal + set -q fish_color_command + or set -U fish_color_command 005fd7 purple + set -q fish_color_param + or set -U fish_color_param 00afff cyan + set -q fish_color_redirection + or set -U fish_color_redirection normal + set -q fish_color_comment + or set -U fish_color_comment red + set -q fish_color_error + or set -U fish_color_error red --bold + set -q fish_color_escape + or set -U fish_color_escape cyan + set -q fish_color_operator + or set -U fish_color_operator cyan + set -q fish_color_end + or set -U fish_color_end green + set -q fish_color_quote + or set -U fish_color_quote brown + set -q fish_color_autosuggestion + or set -U fish_color_autosuggestion 555 yellow + set -q fish_color_user + or set -U fish_color_user green - set -q fish_color_host; or set -U fish_color_host normal - set -q fish_color_valid_path; or set -U fish_color_valid_path --underline + set -q fish_color_host + or set -U fish_color_host normal + set -q fish_color_valid_path + or set -U fish_color_valid_path --underline - set -q fish_color_cwd; or set -U fish_color_cwd green - set -q fish_color_cwd_root; or set -U fish_color_cwd_root red + set -q fish_color_cwd + or set -U fish_color_cwd green + set -q fish_color_cwd_root + or set -U fish_color_cwd_root red - # Background color for matching quotes and parenthesis - set -q fish_color_match; or set -U fish_color_match cyan + # Background color for matching quotes and parenthesis + set -q fish_color_match + or set -U fish_color_match cyan - # Background color for search matches - set -q fish_color_search_match; or set -U fish_color_search_match --background=purple + # Background color for search matches + set -q fish_color_search_match + or set -U fish_color_search_match --background=purple - # Background color for selections - set -q fish_color_selection; or set -U fish_color_selection --background=purple + # Background color for selections + set -q fish_color_selection + or set -U fish_color_selection --background=purple - # Pager colors - set -q fish_pager_color_prefix; or set -U fish_pager_color_prefix cyan - set -q fish_pager_color_completion; or set -U fish_pager_color_completion normal - set -q fish_pager_color_description 555; or set -U fish_pager_color_description 555 yellow - set -q fish_pager_color_progress; or set -U fish_pager_color_progress cyan + # Pager colors + set -q fish_pager_color_prefix + or set -U fish_pager_color_prefix cyan + set -q fish_pager_color_completion + or set -U fish_pager_color_completion normal + set -q fish_pager_color_description 555 + or set -U fish_pager_color_description 555 yellow + set -q fish_pager_color_progress + or set -U fish_pager_color_progress cyan - # - # Directory history colors - # + # + # Directory history colors + # - set -q fish_color_history_current; or set -U fish_color_history_current cyan - end + set -q fish_color_history_current + or set -U fish_color_history_current cyan + end # # Generate man page completions if not present @@ -88,207 +111,210 @@ function __fish_config_interactive -d "Initializations that should be performed eval "$__fish_bin_dir/fish -c 'fish_update_completions > /dev/null ^/dev/null' &" end - # - # Print a greeting - # fish_greeting can be a function (preferred) or a variable - # + # + # Print a greeting + # fish_greeting can be a function (preferred) or a variable + # - if functions -q fish_greeting - fish_greeting - else - # The greeting used to be skipped when fish_greeting was empty (not just undefined) - # Keep it that way to not print superfluous newlines on old configuration - test -n "$fish_greeting"; and echo $fish_greeting - end + if functions -q fish_greeting + fish_greeting + else + # The greeting used to be skipped when fish_greeting was empty (not just undefined) + # Keep it that way to not print superfluous newlines on old configuration + test -n "$fish_greeting" + and echo $fish_greeting + end - # - # This event handler makes sure the prompt is repainted when - # fish_color_cwd changes value. Like all event handlers, it can't be - # autoloaded. - # + # + # This event handler makes sure the prompt is repainted when + # fish_color_cwd changes value. Like all event handlers, it can't be + # autoloaded. + # - function __fish_repaint --on-variable fish_color_cwd --description "Event handler, repaints the prompt when fish_color_cwd changes" - if status --is-interactive - set -e __fish_prompt_cwd - commandline -f repaint ^/dev/null - end - end + function __fish_repaint --on-variable fish_color_cwd --description "Event handler, repaints the prompt when fish_color_cwd changes" + if status --is-interactive + set -e __fish_prompt_cwd + commandline -f repaint ^/dev/null + end + end - function __fish_repaint_root --on-variable fish_color_cwd_root --description "Event handler, repaints the prompt when fish_color_cwd_root changes" - if status --is-interactive - set -e __fish_prompt_cwd - commandline -f repaint ^/dev/null - end - end + function __fish_repaint_root --on-variable fish_color_cwd_root --description "Event handler, repaints the prompt when fish_color_cwd_root changes" + if status --is-interactive + set -e __fish_prompt_cwd + commandline -f repaint ^/dev/null + end + end - # - # Completions for SysV startup scripts. These aren't bound to any - # specific command, so they can't be autoloaded. - # + # + # Completions for SysV startup scripts. These aren't bound to any + # specific command, so they can't be autoloaded. + # - complete -x -p "/etc/init.d/*" -a start --description 'Start service' - complete -x -p "/etc/init.d/*" -a stop --description 'Stop service' - complete -x -p "/etc/init.d/*" -a status --description 'Print service status' - complete -x -p "/etc/init.d/*" -a restart --description 'Stop and then start service' - complete -x -p "/etc/init.d/*" -a reload --description 'Reload service configuration' + complete -x -p "/etc/init.d/*" -a start --description 'Start service' + complete -x -p "/etc/init.d/*" -a stop --description 'Stop service' + complete -x -p "/etc/init.d/*" -a status --description 'Print service status' + complete -x -p "/etc/init.d/*" -a restart --description 'Stop and then start service' + complete -x -p "/etc/init.d/*" -a reload --description 'Reload service configuration' - # Make sure some key bindings are set - if not set -q fish_key_bindings - set -U fish_key_bindings fish_default_key_bindings - end + # Make sure some key bindings are set + if not set -q fish_key_bindings + set -U fish_key_bindings fish_default_key_bindings + end - # Reload key bindings when binding variable change - function __fish_reload_key_bindings -d "Reload key bindings when binding variable change" --on-variable fish_key_bindings - # do nothing if the key bindings didn't actually change - # This could be because the variable was set to the existing value - # or because it was a local variable - if test "$fish_key_bindings" = "$__fish_active_key_bindings" - return - end - set -g __fish_active_key_bindings "$fish_key_bindings" - set -g fish_bind_mode default - if test "$fish_key_bindings" = fish_default_key_bindings - fish_default_key_bindings - else - eval $fish_key_bindings ^/dev/null - end - # Load user key bindings if they are defined - if functions --query fish_user_key_bindings > /dev/null - fish_user_key_bindings - end - end + # Reload key bindings when binding variable change + function __fish_reload_key_bindings -d "Reload key bindings when binding variable change" --on-variable fish_key_bindings + # do nothing if the key bindings didn't actually change + # This could be because the variable was set to the existing value + # or because it was a local variable + if test "$fish_key_bindings" = "$__fish_active_key_bindings" + return + end + set -g __fish_active_key_bindings "$fish_key_bindings" + set -g fish_bind_mode default + if test "$fish_key_bindings" = fish_default_key_bindings + fish_default_key_bindings + else + eval $fish_key_bindings ^/dev/null + end + # Load user key bindings if they are defined + if functions --query fish_user_key_bindings >/dev/null + fish_user_key_bindings + end + end - # Load key bindings. Redirect stderr per #1155 - set -g __fish_active_key_bindings - __fish_reload_key_bindings ^ /dev/null + # Load key bindings. Redirect stderr per #1155 + set -g __fish_active_key_bindings + __fish_reload_key_bindings ^/dev/null - # Repaint screen when window changes size - function __fish_winch_handler --on-signal WINCH - commandline -f repaint - end + # Repaint screen when window changes size + function __fish_winch_handler --on-signal WINCH + commandline -f repaint + end - # Notify vte-based terminals when $PWD changes (issue #906) - if test "$VTE_VERSION" -ge 3405 -o "$TERM_PROGRAM" = "Apple_Terminal" - function __update_vte_cwd --on-variable PWD --description 'Notify VTE of change to $PWD' - status --is-command-substitution; and return - printf '\033]7;file://%s%s\a' (hostname) (pwd | __fish_urlencode) - end - __update_vte_cwd # Run once because we might have already inherited a PWD from an old tab - end + # Notify vte-based terminals when $PWD changes (issue #906) + if test "$VTE_VERSION" -ge 3405 -o "$TERM_PROGRAM" = "Apple_Terminal" + function __update_vte_cwd --on-variable PWD --description 'Notify VTE of change to $PWD' + status --is-command-substitution + and return + printf '\033]7;file://%s%s\a' (hostname) (pwd | __fish_urlencode) + end + __update_vte_cwd # Run once because we might have already inherited a PWD from an old tab + end - ### Command-not-found handlers - # This can be overridden by defining a new __fish_command_not_found_handler function - if not type -q __fish_command_not_found_handler - # First check if we are on OpenSUSE since SUSE's handler has no options - # and expects first argument to be a command and second database - # also check if there is command-not-found command. - if test -f /etc/SuSE-release; and type -q -p command-not-found - function __fish_command_not_found_handler --on-event fish_command_not_found - /usr/bin/command-not-found $argv[1] - end - # Check for Fedora's handler - else if test -f /usr/libexec/pk-command-not-found - function __fish_command_not_found_handler --on-event fish_command_not_found - /usr/libexec/pk-command-not-found $argv[1] - end - # Check in /usr/lib, this is where modern Ubuntus place this command - else if test -f /usr/lib/command-not-found - function __fish_command_not_found_handler --on-event fish_command_not_found - /usr/lib/command-not-found -- $argv[1] - end - # Check for NixOS handler - else if test -f /run/current-system/sw/bin/command-not-found - function __fish_command_not_found_handler --on-event fish_command_not_found - /run/current-system/sw/bin/command-not-found $argv - end - # Ubuntu Feisty places this command in the regular path instead - else if type -q -p command-not-found - function __fish_command_not_found_handler --on-event fish_command_not_found - command-not-found -- $argv[1] - end - # pkgfile is an optional, but official, package on Arch Linux - # it ships with example handlers for bash and zsh, so we'll follow that format - else if type -p -q pkgfile - function __fish_command_not_found_handler --on-event fish_command_not_found - set -l __packages (pkgfile --binaries --verbose -- $argv[1] ^/dev/null) - if test $status -eq 0 - printf "%s may be found in the following packages:\n" "$argv[1]" - printf " %s\n" $__packages - else - __fish_default_command_not_found_handler $argv[1] - end - end - # Use standard fish command not found handler otherwise - else - function __fish_command_not_found_handler --on-event fish_command_not_found - __fish_default_command_not_found_handler $argv[1] - end - end - end + ### Command-not-found handlers + # This can be overridden by defining a new __fish_command_not_found_handler function + if not type -q __fish_command_not_found_handler + # First check if we are on OpenSUSE since SUSE's handler has no options + # and expects first argument to be a command and second database + # also check if there is command-not-found command. + if test -f /etc/SuSE-release + and type -q -p command-not-found + function __fish_command_not_found_handler --on-event fish_command_not_found + /usr/bin/command-not-found $argv[1] + end + # Check for Fedora's handler + else if test -f /usr/libexec/pk-command-not-found + function __fish_command_not_found_handler --on-event fish_command_not_found + /usr/libexec/pk-command-not-found $argv[1] + end + # Check in /usr/lib, this is where modern Ubuntus place this command + else if test -f /usr/lib/command-not-found + function __fish_command_not_found_handler --on-event fish_command_not_found + /usr/lib/command-not-found -- $argv[1] + end + # Check for NixOS handler + else if test -f /run/current-system/sw/bin/command-not-found + function __fish_command_not_found_handler --on-event fish_command_not_found + /run/current-system/sw/bin/command-not-found $argv + end + # Ubuntu Feisty places this command in the regular path instead + else if type -q -p command-not-found + function __fish_command_not_found_handler --on-event fish_command_not_found + command-not-found -- $argv[1] + end + # pkgfile is an optional, but official, package on Arch Linux + # it ships with example handlers for bash and zsh, so we'll follow that format + else if type -p -q pkgfile + function __fish_command_not_found_handler --on-event fish_command_not_found + set -l __packages (pkgfile --binaries --verbose -- $argv[1] ^/dev/null) + if test $status -eq 0 + printf "%s may be found in the following packages:\n" "$argv[1]" + printf " %s\n" $__packages + else + __fish_default_command_not_found_handler $argv[1] + end + end + # Use standard fish command not found handler otherwise + else + function __fish_command_not_found_handler --on-event fish_command_not_found + __fish_default_command_not_found_handler $argv[1] + end + end + end - if test $TERM = "linux" # A linux in-kernel VT with 8 colors and 256/512 glyphs - # In a VT we have - # black (invisible) - # red - # green - # yellow - # blue - # magenta - # cyan - # white + if test $TERM = "linux" # A linux in-kernel VT with 8 colors and 256/512 glyphs + # In a VT we have + # black (invisible) + # red + # green + # yellow + # blue + # magenta + # cyan + # white - # Pretty much just set at random - set -g fish_color_normal normal - set -g fish_color_command yellow - set -g fish_color_param cyan - set -g fish_color_redirection normal - set -g fish_color_comment red - set -g fish_color_error red - set -g fish_color_escape cyan - set -g fish_color_operator cyan - set -g fish_color_quote blue - set -g fish_color_autosuggestion yellow - set -g fish_color_valid_path - set -g fish_color_cwd green - set -g fish_color_cwd_root red - set -g fish_color_match cyan - set -g fish_color_history_current cyan - set -g fish_color_search_match cyan - set -g fish_color_selection blue - set -g fish_color_end yellow - set -g fish_color_host normal - set -g fish_color_status red - set -g fish_color_user green - set -g fish_pager_color_prefix cyan - set -g fish_pager_color_completion normal - set -g fish_pager_color_description yellow - set -g fish_pager_color_progress cyan + # Pretty much just set at random + set -g fish_color_normal normal + set -g fish_color_command yellow + set -g fish_color_param cyan + set -g fish_color_redirection normal + set -g fish_color_comment red + set -g fish_color_error red + set -g fish_color_escape cyan + set -g fish_color_operator cyan + set -g fish_color_quote blue + set -g fish_color_autosuggestion yellow + set -g fish_color_valid_path + set -g fish_color_cwd green + set -g fish_color_cwd_root red + set -g fish_color_match cyan + set -g fish_color_history_current cyan + set -g fish_color_search_match cyan + set -g fish_color_selection blue + set -g fish_color_end yellow + set -g fish_color_host normal + set -g fish_color_status red + set -g fish_color_user green + set -g fish_pager_color_prefix cyan + set -g fish_pager_color_completion normal + set -g fish_pager_color_description yellow + set -g fish_pager_color_progress cyan - # Don't allow setting color other than what linux offers (see #2001) - functions -e set_color - function set_color - set -l term_colors black red green yellow blue magenta cyan white normal - for a in $argv - if not contains -- $a $term_colors - switch $a - # Also allow options - case "-*" - continue - case "*" - echo "Color not valid in TERM = linux: $a" - return 1 - end - end - end - builtin set_color $argv - return $status - end + # Don't allow setting color other than what linux offers (see #2001) + functions -e set_color + function set_color + set -l term_colors black red green yellow blue magenta cyan white normal + for a in $argv + if not contains -- $a $term_colors + switch $a + # Also allow options + case "-*" + continue + case "*" + echo "Color not valid in TERM = linux: $a" + return 1 + end + end + end + builtin set_color $argv + return $status + end - # Set fish_prompt to a VT-friendly version - # without color or unicode - function fish_prompt - fish_fallback_prompt - end - end + # Set fish_prompt to a VT-friendly version + # without color or unicode + function fish_prompt + fish_fallback_prompt + end + end end diff --git a/share/functions/cd.fish b/share/functions/cd.fish index a1b6b2ac9..adf0327ca 100644 --- a/share/functions/cd.fish +++ b/share/functions/cd.fish @@ -15,7 +15,7 @@ function cd --description "Change directory" return $status end - # Avoid set completions + # Avoid set completions. set -l previous $PWD if test "$argv" = "-" @@ -31,7 +31,8 @@ function cd --description "Change directory" set -l cd_status $status if test $cd_status -eq 0 -a "$PWD" != "$previous" - set -q dirprev[$MAX_DIR_HIST]; and set -e dirprev[1] + set -q dirprev[$MAX_DIR_HIST] + and set -e dirprev[1] set -g dirprev $dirprev $previous set -e dirnext set -g __fish_cd_direction prev diff --git a/share/functions/history.fish b/share/functions/history.fish index a26db0f4c..0de39e30b 100644 --- a/share/functions/history.fish +++ b/share/functions/history.fish @@ -1,147 +1,146 @@ # #Deletes an item from history # - function history --description "Deletes an item from history" - set -l argc (count $argv) - set -l prefix_args "" - set -l contains_args "" + set -l argc (count $argv) + set -l prefix_args "" + set -l contains_args "" - set -l cmd print + set -l cmd print - set -l search_mode none + set -l search_mode none - set -l pager less - if set -q PAGER - set pager $PAGER - end + set -l pager less + if set -q PAGER + set pager $PAGER + end - if test $argc -gt 0 - for i in (seq $argc) - switch $argv[$i] - case --delete - set cmd delete - case --prefix - set search_mode prefix - set prefix_args $argv[(math $i + 1)] - case --contains - set search_mode contains - set contains_args $argv[(math $i + 1)] - case --save - set cmd save - case --clear - set cmd clear - case --search - set cmd print - case --merge - set cmd merge - case --help - set cmd help - case -- - set -e argv[$i] - break - case "-*" "--*" - printf ( _ "%s: invalid option -- %s\n" ) history $argv[1] >& 2 - return 1 - end - end - else - #Execute history builtin without any argument - if status --is-interactive - builtin history | eval $pager - else - builtin history - end - return - end + if test $argc -gt 0 + for i in (seq $argc) + switch $argv[$i] + case --delete + set cmd delete + case --prefix + set search_mode prefix + set prefix_args $argv[(math $i + 1)] + case --contains + set search_mode contains + set contains_args $argv[(math $i + 1)] + case --save + set cmd save + case --clear + set cmd clear + case --search + set cmd print + case --merge + set cmd merge + case --help + set cmd help + case -- + set -e argv[$i] + break + case "-*" "--*" + printf ( _ "%s: invalid option -- %s\n" ) history $argv[1] >&2 + return 1 + end + end + else + #Execute history builtin without any argument + if status --is-interactive + builtin history | eval $pager + else + builtin history + end + return + end - switch $cmd - case print - # Print matching items - # Note this may end up passing --search twice to the builtin, - # but that's harmless - builtin history --search $argv + switch $cmd + case print + # Print matching items + # Note this may end up passing --search twice to the builtin, + # but that's harmless + builtin history --search $argv - case delete - # Interactively delete history - set -l found_items "" - switch $search_mode - case prefix - set found_items (builtin history --search --prefix $prefix_args) - case contains - set found_items (builtin history --search --contains $contains_args) - case none - builtin history $argv + case delete + # Interactively delete history + set -l found_items "" + switch $search_mode + case prefix + set found_items (builtin history --search --prefix $prefix_args) + case contains + set found_items (builtin history --search --contains $contains_args) + case none + builtin history $argv - #Save changes after deleting item - builtin history --save - return 0 - end + #Save changes after deleting item + builtin history --save + return 0 + end - set found_items_count (count $found_items) - if test $found_items_count -gt 0 - echo "[0] cancel" - echo "[1] all" - echo + set found_items_count (count $found_items) + if test $found_items_count -gt 0 + echo "[0] cancel" + echo "[1] all" + echo - for i in (seq $found_items_count) - printf "[%s] %s \n" (math $i + 1) $found_items[$i] - end + for i in (seq $found_items_count) + printf "[%s] %s \n" (math $i + 1) $found_items[$i] + end - read --local --prompt "echo 'Delete which entries? > '" choice - set choice (string split " " -- $choice) + read --local --prompt "echo 'Delete which entries? > '" choice + set choice (string split " " -- $choice) - for i in $choice + for i in $choice - # Skip empty input, for example, if the user just hits return - if test -z $i - continue - end + # Skip empty input, for example, if the user just hits return + if test -z $i + continue + end - #Following two validations could be embedded with "and" but I find the syntax kind of weird. - if not string match -qr '^[0-9]+$' $i - printf "Invalid input: %s\n" $i - continue - end + #Following two validations could be embedded with "and" but I find the syntax kind of weird. + if not string match -qr '^[0-9]+$' $i + printf "Invalid input: %s\n" $i + continue + end - if test $i -gt (math $found_items_count + 1) - printf "Invalid input : %s\n" $i - continue - end + if test $i -gt (math $found_items_count + 1) + printf "Invalid input : %s\n" $i + continue + end - if test $i = "0" - printf "Cancel\n" - return - else - if test $i = "1" - for item in $found_items - builtin history --delete $item - end - printf "Deleted all!\n" - else - builtin history --delete $found_items[(math $i - 1)] - end + if test $i = "0" + printf "Cancel\n" + return + else + if test $i = "1" + for item in $found_items + builtin history --delete $item + end + printf "Deleted all!\n" + else + builtin history --delete $found_items[(math $i - 1)] + end - end - end - #Save changes after deleting item(s) - builtin history --save - end - case save - #Save changes to history file - builtin history $argv - case merge - builtin history --merge - case help - builtin history --help - case clear - # Erase the entire history - echo "Are you sure you want to clear history ? (y/n)" - read ch - if test $ch = "y" - builtin history $argv - echo "History cleared!" - end - end + end + end + #Save changes after deleting item(s) + builtin history --save + end + case save + #Save changes to history file + builtin history $argv + case merge + builtin history --merge + case help + builtin history --help + case clear + # Erase the entire history + echo "Are you sure you want to clear history ? (y/n)" + read ch + if test $ch = "y" + builtin history $argv + echo "History cleared!" + end + end end From 14d7b1a0fa4e9890a1d576cc0c5e04cca37e9ec7 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Fri, 6 May 2016 18:51:28 -0700 Subject: [PATCH 243/363] restyle the key_reader source --- src/key_reader.cpp | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/src/key_reader.cpp b/src/key_reader.cpp index 881730771..0d0b997f4 100644 --- a/src/key_reader.cpp +++ b/src/key_reader.cpp @@ -1,10 +1,7 @@ -/* - A small utility to print the resulting key codes from pressing a - key. Servers the same function as hitting ^V in bash, but I prefer - the way key_reader works. - - Type ^C to exit the program. -*/ +// A small utility to print the resulting key codes from pressing a key. Servers the same function +// as hitting ^V in bash, but I prefer the way key_reader works. +// +// Type ^C to exit the program. #include #include #include @@ -48,19 +45,19 @@ int main(int argc, char **argv) { 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); - 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_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 */ + tcsetattr(0, TCSANOW, &modes); // set the new modes while (1) { if ((c = input_common_readch(0)) == EOF) break; if ((c > 31) && (c != 127)) @@ -69,9 +66,7 @@ int main(int argc, char **argv) { sprintf(scratch, "dec: %u hex: %x\n", c, c); writestr(scratch); } - /* reset the terminal to the saved mode */ - tcsetattr(0, TCSANOW, &savemodes); - + tcsetattr(0, TCSANOW, &savemodes); // reset the terminal to the saved mode input_common_destroy(); } From b055b8440c87c69e7f92a81f114ee77f48940f66 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Fri, 6 May 2016 21:22:28 -0700 Subject: [PATCH 244/363] enhance the key_reader program The original `key_reader` program was useful but didn't do much that `xxd` or `od -tx1z` didn't do. Furthermore, it wasn't built and installed by default. This change adds features that make it superior to those programs for decoding interactive key presses and makes it a first-class citizen like the `fish_indent` program that is always available. Fixes #2991 --- .gitignore | 1 + Makefile.in | 16 +-- build_tools/iwyu.osx.imp | 1 + doc_src/fish_key_reader.txt | 40 ++++++ src/fish_key_reader.cpp | 238 ++++++++++++++++++++++++++++++++++++ src/input_common.cpp | 10 +- src/input_common.h | 3 + src/key_reader.cpp | 74 ----------- 8 files changed, 299 insertions(+), 84 deletions(-) create mode 100644 doc_src/fish_key_reader.txt create mode 100644 src/fish_key_reader.cpp delete mode 100644 src/key_reader.cpp diff --git a/.gitignore b/.gitignore index 2420bb4e5..4f123aeeb 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ doc_src/commands.hdr doc_src/index.hdr po/*.gmo fish +fish_key_reader fish_indent fish_tests fish.pc diff --git a/Makefile.in b/Makefile.in index 6172721e8..4bb0998c1 100644 --- a/Makefile.in +++ b/Makefile.in @@ -119,7 +119,7 @@ FISH_TESTS_OBJS := $(FISH_OBJS) obj/fish_tests.o # (that is, are not themselves #included in other source files) # FISH_ALL_OBJS := $(sort $(FISH_OBJS) $(FISH_INDENT_OBJS) $(FISH_TESTS_OBJS) \ - obj/fish.o obj/key_reader.o) + obj/fish.o obj/fish_key_reader.o) # # Files containing user documentation @@ -170,7 +170,7 @@ FUNCTIONS_DIR_FILES := $(wildcard share/functions/*.fish) # # Programs to install # -PROGRAMS := fish fish_indent +PROGRAMS := fish fish_indent fish_key_reader # # Manual pages to install @@ -795,9 +795,11 @@ fish_indent: $(FISH_INDENT_OBJS) $(EXTRA_PCRE2) $(CXX) $(CXXFLAGS) $(LDFLAGS) $(FISH_INDENT_OBJS) $(LIBS) -o $@ # -# Neat little program to show output from terminal +# Build the fish_key_reader program to show input from the terminal. Note that +# fish_key_reader doesn't need all of the object files that fish does but it +# does need a significant number so it's easier to just use the same list. # -key_reader: $(FISH_OBJS) $(EXTRA_PCRE2) obj/key_reader.o +fish_key_reader: $(FISH_OBJS) $(EXTRA_PCRE2) obj/fish_key_reader.o $(CXX) $(CXXFLAGS) $(LDFLAGS_FISH) $^ $(LIBS) -o $@ # @@ -866,7 +868,7 @@ clean: rm -f obj/*.o *.o doc.h doc.tmp rm -f doc_src/*.doxygen doc_src/*.cpp doc_src/*.o doc_src/commands.hdr rm -f tests/tmp.err tests/tmp.out tests/tmp.status tests/foo.txt - rm -f $(PROGRAMS) fish_tests key_reader + rm -f $(PROGRAMS) fish_tests fish_key_reader rm -f command_list.txt command_list_toc.txt toc.txt rm -f doc_src/index.hdr doc_src/commands.hdr rm -f lexicon_filter lexicon.txt lexicon.log @@ -1019,8 +1021,8 @@ obj/io.o: src/fallback.h config.h src/signal.h src/wutil.h src/common.h obj/io.o: src/exec.h src/io.h obj/iothread.o: src/signal.h src/iothread.h src/common.h config.h obj/iothread.o: src/fallback.h -obj/key_reader.o: src/common.h config.h src/fallback.h src/signal.h -obj/key_reader.o: src/input_common.h +obj/fish_key_reader.o: src/common.h config.h src/fallback.h src/signal.h +obj/fish_key_reader.o: src/input_common.h obj/kill.o: src/fallback.h config.h src/signal.h src/kill.h src/common.h obj/kill.o: src/env.h src/exec.h src/path.h obj/output.o: config.h src/fallback.h src/signal.h src/wutil.h src/common.h diff --git a/build_tools/iwyu.osx.imp b/build_tools/iwyu.osx.imp index cad467cdc..ee9353021 100644 --- a/build_tools/iwyu.osx.imp +++ b/build_tools/iwyu.osx.imp @@ -83,6 +83,7 @@ { symbol: ["pid_t", "private", "", "public"] }, { symbol: ["uid_t", "private", "", "public"] }, { symbol: ["gid_t", "private", "", "public"] }, + { symbol: ["timeval", "private", "", "public"] }, { symbol: ["uint32_t", "private", "", "public"] }, { symbol: ["uint32_t", "private", "", "public"] }, { symbol: ["intptr_t", "private", "", "public"] }, diff --git a/doc_src/fish_key_reader.txt b/doc_src/fish_key_reader.txt new file mode 100644 index 000000000..9e5afca64 --- /dev/null +++ b/doc_src/fish_key_reader.txt @@ -0,0 +1,40 @@ +\section fish_key_reader fish_key_reader - explore what characters keyboard keys send + +\subsection fish_key_reader-synopsis Synopsis +\fish{synopsis} +fish_key_reader [-c | --continuous] +\endfish + +\subsection fish_key_reader-description Description + +`fish_key_reader` is used to show in a human friendly manner the sequence of characters each key on a keyboard sends. If the sequence of characters matches a key name recognized by the `bind` command that is also displayed. It shows each characters decimal, hexadecimal and symbolic values. It also shows the delay in microseconds since the previous character was received. The timing data is useful for detecting when an intermediary such as ssh or tmux has altered the timing of the characters sent by the keyboard. If at least 0.2 seconds has passed since the previous character the program will insert a blank line in the output. This makes it visually easier to distinguish the sequence of chars sent by a single key press. + +By default the program exits after displaying a single key sequence. Specifially, it exits after 0.5 seconds has elapsed without seeing another character after the first character is seen. You can force it to run in a continuous mode by passing the `--continuous` or `-c` flag. + +Here is an example of the program in action that also shows how to exit from continuous mode: + +``` +$ ./fish_key_reader --continuous + +Type 'exit' or 'quit' to terminate this program. + +Characters such as [ctrl-D] (EOF) and [ctrl-C] (interrupt) +have no special meaning and will not terminate this program. + +Type 'exit' or 'quit' to terminate this program. + +999999 usec dec: 27 hex: 1b char: \e (aka \c[) + 450 usec dec: 91 hex: 5b char: [ + 409 usec dec: 49 hex: 31 char: 1 + 424 usec dec: 126 hex: 7e char: ~ +FYI: Found sequence for bind key name "home" + +Type 'exit' or 'quit' to terminate this program. + +999999 usec dec: 113 hex: 71 char: q +111562 usec dec: 117 hex: 75 char: u + 55820 usec dec: 105 hex: 69 char: i +128021 usec dec: 116 hex: 74 char: t + +Exiting at your request. +``` diff --git a/src/fish_key_reader.cpp b/src/fish_key_reader.cpp new file mode 100644 index 000000000..ef587d3c3 --- /dev/null +++ b/src/fish_key_reader.cpp @@ -0,0 +1,238 @@ +// A small utility to print information related to pressing keys. This is similar to using tools +// like `xxd` and `od -tx1z` but provides more information such as the time delay between each +// character. It also allows pressing and interpreting keys that are normally special such as +// [ctrl-C] (interrupt the program) or [ctrl-D] (EOF to signal the program should exit). +// And unlike those other tools this one disables ICRNL mode so it can distinguish between +// carriage-return (\cM) and newline (\cJ). +// +// Type "exit" or "quit" to terminate the program. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "env.h" +#include "fallback.h" // IWYU pragma: keep +#include "input.h" +#include "input_common.h" + +struct config_paths_t determine_config_directory_paths(const char *argv0); + +static struct termios saved_modes; // so we can reset the modes when we're done +static long long int prev_tstamp = 0; +static const char *ctrl_equivalents[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, "\\a", + "\\b", "\\t", "\\n", "\\v", "\\f", "\\r", NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, "\\e", NULL, NULL, NULL, NULL}; + +/// Return true if the recent sequence of characters indicates the user wants to exit the program. +bool should_exit(unsigned char c) { + static unsigned char recent_chars[4] = {0}; + + recent_chars[0] = recent_chars[1]; + recent_chars[1] = recent_chars[2]; + recent_chars[2] = recent_chars[3]; + recent_chars[3] = c; + return memcmp(recent_chars, "exit", 4) == 0 || memcmp(recent_chars, "quit", 4) == 0; +} + +/// Return the key name if the recent sequence of characters matches a known terminfo sequence. +char * const key_name(unsigned char c) { + static char recent_chars[8] = {0}; + + recent_chars[0] = recent_chars[1]; + recent_chars[1] = recent_chars[2]; + recent_chars[2] = recent_chars[3]; + recent_chars[3] = recent_chars[4]; + recent_chars[4] = recent_chars[5]; + recent_chars[5] = recent_chars[6]; + recent_chars[6] = recent_chars[7]; + recent_chars[7] = c; + + for (int idx = 7; idx >= 0; idx--) { + wcstring out_name; + wcstring seq = str2wcstring(recent_chars + idx, 8 - idx); + bool found = input_terminfo_get_name(seq, &out_name); + if (found) { + return strdup(wcs2string(out_name).c_str()); + } + } + + return NULL; +} + +/// Process the characters we receive as the user presses keys. +void process_input(bool continuous_mode) { + bool first_char_seen = false; + while (true) { + wchar_t wc = input_common_readch(first_char_seen && !continuous_mode); + if (wc == WEOF) { + return; + } + if (wc > 255) { + printf("\nUnexpected wide character from input_common_readch(): %lld / 0x%llx\n", + (long long)wc, (long long)wc); + return; + } + + long long int curr_tstamp, delta_tstamp; + timeval char_tstamp; + gettimeofday(&char_tstamp, NULL); + curr_tstamp = char_tstamp.tv_sec * 1000000 + char_tstamp.tv_usec; + delta_tstamp = curr_tstamp - prev_tstamp; + if (delta_tstamp >= 1000000) delta_tstamp = 999999; + if (delta_tstamp >= 200000 && continuous_mode) { + printf("\n"); + printf("Type 'exit' or 'quit' to terminate this program.\n"); + printf("\n"); + } + prev_tstamp = curr_tstamp; + + unsigned char c = wc; + if (c < 32) { + // Control characters. + if (ctrl_equivalents[c]) { + printf("%6lld usec dec: %3u hex: %2x char: %s (aka \\c%c)\n", delta_tstamp, c, c, + ctrl_equivalents[c], c + 64); + } else { + printf("%6lld usec dec: %3u hex: %2x char: \\c%c\n", delta_tstamp, c, c, c + 64); + } + } else if (c == 32) { + // The space character. + printf("%6lld usec dec: %3u hex: %2x char: \n", delta_tstamp, c, c); + } else if (c == 127) { + // The "del" character. + printf("%6lld usec dec: %3u hex: %2x char: \\x7f (aka del)\n", delta_tstamp, c, c); + } else if (c >= 128) { + // Non-ASCII characters (i.e., those with bit 7 set). + printf("%6lld usec dec: %3u hex: %2x char: non-ASCII\n", delta_tstamp, c, c); + } else { + // ASCII characters that are not control characters. + printf("%6lld usec dec: %3u hex: %2x char: %c\n", delta_tstamp, c, c, c); + } + + char * const name = key_name(c); + if (name) { + printf("FYI: Saw sequence for bind key name \"%s\"\n", name); + free(name); + } + + if (should_exit(c)) { + printf("\nExiting at your request.\n"); + break; + } + + first_char_seen = true; + } +} + +/// Set the tty modes to not interpret any characters. We want every character to be passed thru to +/// this program. Including characters such as [ctrl-C] and [ctrl-D] that might normally have +/// special significance (e.g., terminate the program). +bool set_tty_modes(void) { + struct termios modes; + + tcgetattr(0, &modes); // get the current tty modes + saved_modes = modes; // save a copy so we can reset them on exit + + modes.c_lflag &= ~ICANON; // turn off canonical mode + modes.c_lflag &= ~ECHO; // turn off echo mode + modes.c_lflag &= ~ISIG; // turn off recognizing signal generating characters + modes.c_iflag &= ~ICRNL; // turn off mapping CR to NL + modes.c_iflag &= ~INLCR; // turn off mapping NL to CR + modes.c_cc[VMIN] = 1; // return each character as they arrive + modes.c_cc[VTIME] = 0; // wait forever for the next character + + if (tcsetattr(0, TCSANOW, &modes) != 0) { // set the new modes + return false; + } + return true; +} + +/// Restore the tty modes to what they were before this program was run. This shouldn't be required +/// but we do it just in case the program that ran us doesn't handle tty modes for external programs +/// in a sensible manner. +void reset_tty_modes() { tcsetattr(0, TCSANOW, &saved_modes); } + +/// Make sure we cleanup before exiting if we're signaled. +void signal_handler(int signo) { + printf("\nExiting on receipt of signal #%d\n", signo); + reset_tty_modes(); + exit(1); +} + +/// Setup our environment (e.g., tty modes), process key strokes, then reset the environment. +void setup_and_process_keys(bool continuous_mode) { + set_main_thread(); + setup_fork_guards(); + wsetlocale(LC_ALL, L"POSIX"); + program_name = L"fish_key_reader"; + env_init(); + input_init(); + + // Installing our handler for every signal (e.g., SIGSEGV) is dubious because it means that + // signals that might generate a core dump will not do so. On the other hand this allows us + // to restore the tty modes so the terminal is still usable when we die. + for (int signo = 1; signo < 32; signo++) { + signal(signo, signal_handler); + } + + if (continuous_mode) { + printf("\n"); + printf("Type 'exit' or 'quit' to terminate this program.\n"); + printf("\n"); + printf("Characters such as [ctrl-D] (EOF) and [ctrl-C] (interrupt)\n"); + printf("have no special meaning and will not terminate this program.\n"); + printf("\n"); + } else { + set_wait_on_escape_ms(500); + } + + if (!set_tty_modes()) { + printf("Could not set the tty modes. Refusing to continue running.\n"); + exit(1); + } + // TODO: We really should enable keypad mode but see issue #838. + process_input(continuous_mode); + reset_tty_modes(); +} + +int main(int argc, char **argv) { + bool continuous_mode = false; + const char *short_opts = "+c"; + const struct option long_opts[] = {{"continuous", no_argument, NULL, 'd'}, {NULL, 0, NULL, 0}}; + int opt; + while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) { + switch (opt) { + case 0: { + fprintf(stderr, "getopt_long() unexpectedly returned zero\n"); + exit(1); + } + case 'c': { + continuous_mode = true; + break; + } + default: { + // We assume getopt_long() has already emitted a diagnostic msg. + exit(1); + } + } + } + + argc -= optind; + if (argc != 0) { + fprintf(stderr, "Expected no CLI arguments, got %d\n", argc); + return 1; + } + + setup_and_process_keys(continuous_mode); + return 0; +} diff --git a/src/input_common.cpp b/src/input_common.cpp index 93e94fd3f..405e0d6e4 100644 --- a/src/input_common.cpp +++ b/src/input_common.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -28,9 +29,9 @@ #include "iothread.h" #include "util.h" -// Time in milliseconds to wait for another byte to be available for reading -// after \x1b is read before assuming that escape key was pressed, and not an -// escape sequence. +/// Time in milliseconds to wait for another byte to be available for reading +/// after \x1b is read before assuming that escape key was pressed, and not an +/// escape sequence. #define WAIT_ON_ESCAPE_DEFAULT 300 static int wait_on_escape_ms = WAIT_ON_ESCAPE_DEFAULT; @@ -169,6 +170,9 @@ static wint_t readb() { return arr[0]; } +// Directly set the input timeout. +void set_wait_on_escape_ms(int ms) { wait_on_escape_ms = ms; } + // Update the wait_on_escape_ms value in response to the fish_escape_delay_ms user variable being // set. void update_wait_on_escape_ms() { diff --git a/src/input_common.h b/src/input_common.h index d7bafe3fa..e5778b70f 100644 --- a/src/input_common.h +++ b/src/input_common.h @@ -85,6 +85,9 @@ void input_common_destroy(); /// Adjust the escape timeout. void update_wait_on_escape_ms(); +/// Set the escape timeout directly. +void set_wait_on_escape_ms(int ms); + /// Function used by input_readch to read bytes from stdin until enough bytes have been read to /// convert them to a wchar_t. Conversion is done using mbrtowc. If a character has previously been /// read and then 'unread' using \c input_common_unreadch, that character is returned. If timed is diff --git a/src/key_reader.cpp b/src/key_reader.cpp deleted file mode 100644 index 0d0b997f4..000000000 --- a/src/key_reader.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// A small utility to print the resulting key codes from pressing a key. Servers the same function -// as hitting ^V in bash, but I prefer the way key_reader works. -// -// Type ^C to exit the program. -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "fallback.h" // IWYU pragma: keep -#include "input_common.h" - -int writestr(char *str) { - write_ignore(1, str, strlen(str)); - return 0; -} - -int main(int argc, char **argv) { - set_main_thread(); - setup_fork_guards(); - setlocale(LC_ALL, ""); - - if (argc == 2) { - static char term_buffer[2048]; - char *termtype = getenv("TERM"); - char *tbuff = new char[9999]; - char *res; - - tgetent(term_buffer, termtype); - res = tgetstr(argv[1], &tbuff); - if (res != 0) { - while (*res != 0) { - printf("%d ", *res); - - res++; - } - printf("\n"); - } else { - printf("Undefined sequence\n"); - } - } else { - 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 - - input_common_init(0); - - tcgetattr(0, &modes); // get the current terminal modes - savemodes = modes; // save a copy so we can reset them - - modes.c_lflag &= ~ICANON; // turn off canonical mode - modes.c_lflag &= ~ECHO; // turn off echo mode - modes.c_cc[VMIN] = 1; - modes.c_cc[VTIME] = 0; - tcsetattr(0, TCSANOW, &modes); // set the new modes - while (1) { - if ((c = input_common_readch(0)) == EOF) break; - if ((c > 31) && (c != 127)) - sprintf(scratch, "dec: %u hex: %x char: %c\n", c, c, c); - else - sprintf(scratch, "dec: %u hex: %x\n", c, c); - writestr(scratch); - } - tcsetattr(0, TCSANOW, &savemodes); // reset the terminal to the saved mode - input_common_destroy(); - } - - return 0; -} From 4244a6e6fef9c466c2f3f93d9da9af3abc4a73ac Mon Sep 17 00:00:00 2001 From: Terje Larsen Date: Wed, 11 May 2016 13:33:22 +0200 Subject: [PATCH 245/363] Add git commit --fixup completions (#3021) --- share/completions/git.fish | 2 ++ 1 file changed, 2 insertions(+) diff --git a/share/completions/git.fish b/share/completions/git.fish index d8e7b1a83..f8c644586 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -335,6 +335,8 @@ complete -f -c git -n '__fish_git_using_command clone' -l recursive -d 'Initiali complete -c git -n '__fish_git_needs_command' -a commit -d 'Record changes to the repository' complete -c git -n '__fish_git_using_command commit' -l amend -d 'Amend the log message of the last commit' complete -f -c git -n '__fish_git_using_command commit' -a '(__fish_git_modified_files)' +complete -f -c git -n '__fish_git_using_command commit' -l fixup -d 'Fixup commit to be used with rebase --autosquash' +complete -f -c git -n '__fish_git_using_command commit; and __fish_contains_opt fixup' -a '(__fish_git_commits)' # TODO options ### diff From a7605d584b91e0caf7fa5454dbd3fa1e4204dc7e Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 11 May 2016 14:51:54 +0200 Subject: [PATCH 246/363] git completion: Show commits for revert and tag --contains --- share/completions/git.fish | 2 ++ 1 file changed, 2 insertions(+) diff --git a/share/completions/git.fish b/share/completions/git.fish index f8c644586..ac4d82490 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -470,6 +470,7 @@ complete -f -c git -n '__fish_git_using_command reset' -a '(__fish_git_staged_fi ### revert complete -f -c git -n '__fish_git_needs_command' -a revert -d 'Revert an existing commit' +complete -f -c git -n '__fish_git_using_command revert' -a '(__fish_git_commits)' # TODO options ### rm @@ -502,6 +503,7 @@ complete -f -c git -n '__fish_git_using_command tag' -s d -l delete -d 'Remove a complete -f -c git -n '__fish_git_using_command tag' -s v -l verify -d 'Verify signature of a tag' complete -f -c git -n '__fish_git_using_command tag' -s f -l force -d 'Force overwriting exising tag' complete -f -c git -n '__fish_git_using_command tag' -s l -l list -d 'List tags' +complete -f -c git -n '__fish_git_using_command tag' -l contains -xa '(__fish_git_commits)' -d 'List tags that contain a commit' complete -f -c git -n '__fish_git_using_command tag; and __fish_contains_opt -s d' -a '(__fish_git_tags)' -d 'Tag' complete -f -c git -n '__fish_git_using_command tag; and __fish_contains_opt -s v' -a '(__fish_git_tags)' -d 'Tag' # TODO options From 76d24aa1bc423d906167470bf3b68e9c1ef0ea91 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 11 May 2016 14:56:36 +0200 Subject: [PATCH 247/363] git completion: Allow more than one arg to using_command Now we can easily add an option to multiple commmands. This should also fix some edgecases. --- share/completions/git.fish | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index ac4d82490..e2c532517 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -94,6 +94,7 @@ function __fish_git_needs_command return 1 # We assume that any other token that's not an argument to a general option is a command case "*" + echo $c return 1 end end @@ -103,19 +104,14 @@ function __fish_git_needs_command end function __fish_git_using_command - set cmd (commandline -opc) - if [ (count $cmd) -gt 1 ] - if [ $argv[1] = $cmd[2] ] - return 0 - end + set -l cmd (__fish_git_needs_command) + test -z "$cmd"; and return 1 + contains -- $cmd $argv; and return 0 # aliased command - set -l aliased (command git config --get "alias.$cmd[2]" ^ /dev/null | string split " ") - if [ $argv[1] = "$aliased[1]" ] - return 0 - end - end - return 1 + set -l aliased (command git config --get "alias.$cmd" ^/dev/null | string split " ") + contains -- "$aliased[1]" $argv; and return 0 + return 1 end function __fish_git_stash_using_command From a38d5504acfd5126af7ab93fe660127428da7492 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 11 May 2016 14:53:16 +0200 Subject: [PATCH 248/363] git completion: Allow --pretty for more commands This _should_ be all of them. --- share/completions/git.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index e2c532517..19da0f982 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -184,6 +184,7 @@ end # general options complete -f -c git -l help -d 'Display the manual of a git command' +complete -f -c git -n '__fish_git_using_command log show diff-tree rev-list' -l pretty -a 'oneline short medium full fuller email raw format:' #### fetch complete -f -c git -n '__fish_git_needs_command' -a fetch -d 'Download objects and refs from another repository' @@ -360,7 +361,6 @@ complete -f -c git -n '__fish_git_needs_command' -a init -d 'Create an empty git ### log complete -c git -n '__fish_git_needs_command' -a log -d 'Show commit logs' complete -c git -n '__fish_git_using_command log' -a '(__fish_git_heads) (__fish_git_ranges)' -d 'Branch' -complete -f -c git -n '__fish_git_using_command log' -l pretty -a 'oneline short medium full fuller email raw format:' # TODO options ### merge From d2e79cf6f6589ae2cd8fb6ad1ac294f4486b0a2c Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 11 May 2016 14:54:17 +0200 Subject: [PATCH 249/363] git completion: More stringification --- share/completions/git.fish | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index 19da0f982..ec604ed40 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -133,18 +133,13 @@ function __fish_git_stash_not_using_subcommand end function __fish_git_complete_stashes - set -l IFS ':' - command git stash list --format=%gd:%gs ^/dev/null | while read -l name desc - echo $name\t$desc - end + command git stash list --format=%gd:%gs ^/dev/null | string replace ":" \t end function __fish_git_aliases - set -l IFS \n command git config -z --get-regexp '^alias\.' ^/dev/null | while read -lz key value begin - set -l IFS "." - echo -n $key | read -l _ name + set -l name (string replace -r '^.*\.' '' -- $key) printf "%s\t%s\n" $name "Alias for $value" end end From b60ef72c3d9f7585d30c141e54dd7e9665d302cd Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 11 May 2016 14:57:56 +0200 Subject: [PATCH 250/363] git completion: Fix option-before-command for stash Because it allows sub-subcommands, it has functions to determine which, if any, is used. These were too simplistic. --- share/completions/git.fish | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index ec604ed40..d23de7dd3 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -115,21 +115,22 @@ function __fish_git_using_command end function __fish_git_stash_using_command - set cmd (commandline -opc) - if [ (count $cmd) -gt 2 ] - if [ $cmd[2] = 'stash' -a $argv[1] = $cmd[3] ] - return 0 - end - end - return 1 + set cmd (commandline -opc) + __fish_git_using_command stash; or return 2 + # The word after the stash command _must_ be the subcommand + set cmd $cmd[(contains -i -- "stash" $cmd)..-1] + set -e cmd[1] + set -q cmd[1]; or return 1 + contains -- $cmd[1] $argv; and return 0 + return 1 end function __fish_git_stash_not_using_subcommand - set cmd (commandline -opc) - if [ (count $cmd) -gt 2 -a $cmd[2] = 'stash' ] - return 1 - end - return 0 + set cmd (commandline -opc) + __fish_git_using_command stash; or return 2 + set cmd $cmd[(contains -i -- "stash" $cmd)..-1] + set -q cmd[2]; and return 1 + return 0 end function __fish_git_complete_stashes From fbe2cdc3c7766bcb37024964b49d1749802b24f7 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 11 May 2016 15:01:24 +0200 Subject: [PATCH 251/363] git completion: Complete commits for cherry-pick if at least three characters are given --- share/completions/git.fish | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/share/completions/git.fish b/share/completions/git.fish index d23de7dd3..bdea33aa1 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -178,6 +178,16 @@ function __fish_git_branch_for_remote __fish_git_branches | string match -- "$remote/*" | string replace -- "$remote/" '' end +# Return 0 if the current token is a possible commit-hash with at least 3 characters +function __fish_git_possible_commithash + set -q argv[1]; and set -l token $argv[1] + or set -l token (commandline -ct) + if string match -qr '^[0-9a-fA-F]{3,}$' -- $token + return 0 + end + return 1 +end + # general options complete -f -c git -l help -d 'Display the manual of a git command' complete -f -c git -n '__fish_git_using_command log show diff-tree rev-list' -l pretty -a 'oneline short medium full fuller email raw format:' @@ -305,6 +315,8 @@ complete -f -c git -n '__fish_git_using_command branch' -l no-merged -d 'List br complete -f -c git -n '__fish_git_needs_command' -a cherry-pick -d 'Apply the change introduced by an existing commit' complete -f -c git -n '__fish_git_using_command cherry-pick' -a '(__fish_git_branches --no-merged)' -d 'Branch' complete -f -c git -n '__fish_git_using_command cherry-pick' -a '(__fish_git_unique_remote_branches --no-merged)' -d 'Remote branch' +# TODO: Filter further +complete -f -c git -n '__fish_git_using_command cherry-pick; and __fish_git_possible_commithash' -a '(__fish_git_commits)' complete -f -c git -n '__fish_git_using_command cherry-pick' -s e -l edit -d 'Edit the commit message prior to committing' complete -f -c git -n '__fish_git_using_command cherry-pick' -s x -d 'Append info in generated commit on the origin of the cherry-picked change' complete -f -c git -n '__fish_git_using_command cherry-pick' -s n -l no-commit -d 'Apply changes without making any commit' From c63c88262baee0a1a0427c1b8b1a3fac2b2ceee5 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 11 May 2016 15:06:10 +0200 Subject: [PATCH 252/363] Indent git completion It's about time I finally fixed my emacs config. --- share/completions/git.fish | 219 +++++++++++++++++++------------------ 1 file changed, 115 insertions(+), 104 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index bdea33aa1..234db0d7c 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -2,134 +2,143 @@ # Use 'command git' to avoid interactions for aliases from git to (e.g.) hub function __fish_git_commits - # Complete commits with their subject line as the description - # This allows filtering by subject with the new pager! - # Because even subject lines can be quite long, - # trim them (abbrev'd hash+tab+subject) to 70 characters - command git log --pretty=tformat:"%h"\t"%s" --all \ - | string replace -r '(.{70}).+' '$1...' + # Complete commits with their subject line as the description + # This allows filtering by subject with the new pager! + # Because even subject lines can be quite long, + # trim them (abbrev'd hash+tab+subject) to 70 characters + command git log --pretty=tformat:"%h"\t"%s" --all | string replace -r '(.{70}).+' '$1...' end function __fish_git_branches - command git branch --no-color -a $argv ^/dev/null | string match -r -v ' -> ' | string trim -c "* " | string replace -r "^remotes/" "" + command git branch --no-color -a $argv ^/dev/null | string match -r -v ' -> ' | string trim -c "* " | string replace -r "^remotes/" "" end function __fish_git_unique_remote_branches - # Allow all remote branches with one remote without the remote part - # This is useful for `git checkout` to automatically create a remote-tracking branch - command git branch --no-color -a $argv ^/dev/null | string match -r -v ' -> ' | string trim -c "* " | string replace -r "^remotes/[^/]*/" "" | sort | uniq -u + # Allow all remote branches with one remote without the remote part + # This is useful for `git checkout` to automatically create a remote-tracking branch + command git branch --no-color -a $argv ^/dev/null | string match -r -v ' -> ' | string trim -c "* " | string replace -r "^remotes/[^/]*/" "" | sort | uniq -u end function __fish_git_tags - command git tag ^/dev/null + command git tag ^/dev/null end function __fish_git_heads - __fish_git_branches - __fish_git_tags + __fish_git_branches + __fish_git_tags end function __fish_git_remotes - command git remote ^/dev/null + command git remote ^/dev/null end function __fish_git_modified_files - # git diff --name-only hands us filenames relative to the git toplevel - set -l root (command git rev-parse --show-toplevel) - # Print files from the current $PWD as-is, prepend all others with ":/" (relative to toplevel in git-speak) - # This is a bit simplistic but finding the lowest common directory and then replacing everything else in $PWD with ".." is a bit annoying - string replace -- "$PWD/" "" "$root/"(command git diff --name-only ^/dev/null) | string replace "$root/" ":/" + # git diff --name-only hands us filenames relative to the git toplevel + set -l root (command git rev-parse --show-toplevel) + # Print files from the current $PWD as-is, prepend all others with ":/" (relative to toplevel in git-speak) + # This is a bit simplistic but finding the lowest common directory and then replacing everything else in $PWD with ".." is a bit annoying + string replace -- "$PWD/" "" "$root/"(command git diff --name-only ^/dev/null) | string replace "$root/" ":/" end function __fish_git_staged_files - set -l root (command git rev-parse --show-toplevel) - string replace -- "$PWD/" "" "$root/"(command git diff --staged --name-only ^/dev/null) | string replace "$root/" ":/" + set -l root (command git rev-parse --show-toplevel) + string replace -- "$PWD/" "" "$root/"(command git diff --staged --name-only ^/dev/null) | string replace "$root/" ":/" end function __fish_git_add_files - set -l root (command git rev-parse --show-toplevel) - string replace -- "$PWD/" "" "$root/"(command git -C $root ls-files -mo --exclude-standard ^/dev/null) | string replace "$root/" ":/" + set -l root (command git rev-parse --show-toplevel) + string replace -- "$PWD/" "" "$root/"(command git -C $root ls-files -mo --exclude-standard ^/dev/null) | string replace "$root/" ":/" end function __fish_git_ranges - set -l both (commandline -ot | string split "..") - set -l from $both[1] - # If we didn't need to split (or there's nothing _to_ split), complete only the first part - # Note that status here is from `string split` because `set` doesn't alter it - if test -z "$from" -o $status -gt 0 - __fish_git_heads - return 0 - end + set -l both (commandline -ot | string split "..") + set -l from $both[1] + # If we didn't need to split (or there's nothing _to_ split), complete only the first part + # Note that status here is from `string split` because `set` doesn't alter it + if test -z "$from" -o $status -gt 0 + __fish_git_heads + return 0 + end - set -l to (set -q both[2]; and echo $both[2]) - for from_ref in (__fish_git_heads | string match "$from") - for to_ref in (__fish_git_heads | string match "*$to*") # if $to is empty, this correctly matches everything - printf "%s..%s\n" $from_ref $to_ref - end - end + set -l to (set -q both[2]; and echo $both[2]) + for from_ref in (__fish_git_heads | string match "$from") + for to_ref in (__fish_git_heads | string match "*$to*") # if $to is empty, this correctly matches everything + printf "%s..%s\n" $from_ref $to_ref + end + end end function __fish_git_needs_command - set cmd (commandline -opc) - if [ (count $cmd) -eq 1 ] - return 0 - else - set -l skip_next 1 - # Skip first word because it's "git" or a wrapper - for c in $cmd[2..-1] - test $skip_next -eq 0; and set skip_next 1; and continue - # git can only take a few options before a command, these are the ones mentioned in the "git" man page - # e.g. `git --follow log` is wrong, `git --help log` is okay (and `git --help log $branch` is superfluous but works) - # In case any other option is used before a command, we'll fail, but that's okay since it's invalid anyway - switch $c - # General options that can still take a command - case "--help" "-p" "--paginate" "--no-pager" "--bare" "--no-replace-objects" --{literal,glob,noglob,icase}-pathspecs --{exec-path,git-dir,work-tree,namespace}"=*" - continue - # General options with an argument we need to skip. The option=value versions have already been handled above - case --{exec-path,git-dir,work-tree,namespace} - set skip_next 0 - continue - # General options that cause git to do something and exit - these behave like commands and everything after them is ignored - case "--version" --{html,man,info}-path - return 1 - # We assume that any other token that's not an argument to a general option is a command - case "*" - echo $c - return 1 - end - end - return 0 - end - return 1 + set cmd (commandline -opc) + if [ (count $cmd) -eq 1 ] + return 0 + else + set -l skip_next 1 + # Skip first word because it's "git" or a wrapper + for c in $cmd[2..-1] + test $skip_next -eq 0 + and set skip_next 1 + and continue + # git can only take a few options before a command, these are the ones mentioned in the "git" man page + # e.g. `git --follow log` is wrong, `git --help log` is okay (and `git --help log $branch` is superfluous but works) + # In case any other option is used before a command, we'll fail, but that's okay since it's invalid anyway + switch $c + # General options that can still take a command + case "--help" "-p" "--paginate" "--no-pager" "--bare" "--no-replace-objects" --{literal,glob,noglob,icase}-pathspecs --{exec-path,git-dir,work-tree,namespace}"=*" + continue + # General options with an argument we need to skip. The option=value versions have already been handled above + case --{exec-path,git-dir,work-tree,namespace} + set skip_next 0 + continue + # General options that cause git to do something and exit - these behave like commands and everything after them is ignored + case "--version" --{html,man,info}-path + return 1 + # We assume that any other token that's not an argument to a general option is a command + case "*" + echo $c + return 1 + end + end + return 0 + end + return 1 end function __fish_git_using_command set -l cmd (__fish_git_needs_command) - test -z "$cmd"; and return 1 - contains -- $cmd $argv; and return 0 + test -z "$cmd" + and return 1 + contains -- $cmd $argv + and return 0 # aliased command set -l aliased (command git config --get "alias.$cmd" ^/dev/null | string split " ") - contains -- "$aliased[1]" $argv; and return 0 + contains -- "$aliased[1]" $argv + and return 0 return 1 end function __fish_git_stash_using_command set cmd (commandline -opc) - __fish_git_using_command stash; or return 2 + __fish_git_using_command stash + or return 2 # The word after the stash command _must_ be the subcommand set cmd $cmd[(contains -i -- "stash" $cmd)..-1] set -e cmd[1] - set -q cmd[1]; or return 1 - contains -- $cmd[1] $argv; and return 0 + set -q cmd[1] + or return 1 + contains -- $cmd[1] $argv + and return 0 return 1 end function __fish_git_stash_not_using_subcommand set cmd (commandline -opc) - __fish_git_using_command stash; or return 2 + __fish_git_using_command stash + or return 2 set cmd $cmd[(contains -i -- "stash" $cmd)..-1] - set -q cmd[2]; and return 1 + set -q cmd[2] + and return 1 return 0 end @@ -153,7 +162,7 @@ function __fish_git_custom_commands # if any of these completion results match the name of the builtin git commands, # but it's simpler just to blacklist these names. They're unlikely to change, # and the failure mode is we accidentally complete a plumbing command. - for name in (string replace -r "^.*/git-([^/]*)" '$1' $PATH/git-*) + for name in (string replace -r "^.*/git-([^/]*)" '$1' $PATH/git-*) switch $name case cvsserver receive-pack shell upload-archive upload-pack # skip these @@ -165,22 +174,24 @@ end # Suggest branches for the specified remote - returns 1 if no known remote is specified function __fish_git_branch_for_remote - set -l remotes (__fish_git_remotes) - set -l remote - set -l cmd (commandline -opc) - for r in $remotes - if contains -- $r $cmd - set remote $r - break - end - end - set -q remote[1]; or return 1 - __fish_git_branches | string match -- "$remote/*" | string replace -- "$remote/" '' + set -l remotes (__fish_git_remotes) + set -l remote + set -l cmd (commandline -opc) + for r in $remotes + if contains -- $r $cmd + set remote $r + break + end + end + set -q remote[1] + or return 1 + __fish_git_branches | string match -- "$remote/*" | string replace -- "$remote/" '' end # Return 0 if the current token is a possible commit-hash with at least 3 characters function __fish_git_possible_commithash - set -q argv[1]; and set -l token $argv[1] + set -q argv[1] + and set -l token $argv[1] or set -l token (commandline -ct) if string match -qr '^[0-9a-fA-F]{3,}$' -- $token return 0 @@ -258,7 +269,7 @@ complete -f -c git -n '__fish_git_using_command show-branch' -a '(__fish_git_hea # TODO options ### add -complete -c git -n '__fish_git_needs_command' -a add -d 'Add file contents to the index' +complete -c git -n '__fish_git_needs_command' -a add -d 'Add file contents to the index' complete -c git -n '__fish_git_using_command add' -s n -l dry-run -d "Don't actually add the file(s)" complete -c git -n '__fish_git_using_command add' -s v -l verbose -d 'Be verbose' complete -c git -n '__fish_git_using_command add' -s f -l force -d 'Allow adding otherwise ignored files' @@ -276,10 +287,10 @@ complete -f -c git -n '__fish_git_using_command add' -a '(__fish_git_add_files)' # TODO options ### checkout -complete -f -c git -n '__fish_git_needs_command' -a checkout -d 'Checkout and switch to a branch' -complete -f -c git -n '__fish_git_using_command checkout' -a '(__fish_git_branches)' --description 'Branch' -complete -f -c git -n '__fish_git_using_command checkout' -a '(__fish_git_unique_remote_branches)' --description 'Remote branch' -complete -f -c git -n '__fish_git_using_command checkout' -a '(__fish_git_tags)' --description 'Tag' +complete -f -c git -n '__fish_git_needs_command' -a checkout -d 'Checkout and switch to a branch' +complete -f -c git -n '__fish_git_using_command checkout' -a '(__fish_git_branches)' --description 'Branch' +complete -f -c git -n '__fish_git_using_command checkout' -a '(__fish_git_unique_remote_branches)' --description 'Remote branch' +complete -f -c git -n '__fish_git_using_command checkout' -a '(__fish_git_tags)' --description 'Tag' complete -f -c git -n '__fish_git_using_command checkout' -a '(__fish_git_modified_files)' --description 'File' complete -f -c git -n '__fish_git_using_command checkout' -s b -d 'Create a new branch' complete -f -c git -n '__fish_git_using_command checkout' -s t -l track -d 'Track a new branch' @@ -326,7 +337,7 @@ complete -f -c git -n '__fish_git_using_command cherry-pick' -l ff -d 'Fast-forw ### clone complete -f -c git -n '__fish_git_needs_command' -a clone -d 'Clone a repository into a new directory' complete -f -c git -n '__fish_git_using_command clone' -l no-hardlinks -d 'Copy files instead of using hardlinks' -complete -f -c git -n '__fish_git_using_command clone' -s q -l quiet -d 'Operate quietly and do not report progress' +complete -f -c git -n '__fish_git_using_command clone' -s q -l quiet -d 'Operate quietly and do not report progress' complete -f -c git -n '__fish_git_using_command clone' -s v -l verbose -d 'Provide more information on what is going on' complete -f -c git -n '__fish_git_using_command clone' -s n -l no-checkout -d 'No checkout of HEAD is performed after the clone is complete' complete -f -c git -n '__fish_git_using_command clone' -l bare -d 'Make a bare Git repository' @@ -337,7 +348,7 @@ complete -f -c git -n '__fish_git_using_command clone' -l depth -d 'Truncate the complete -f -c git -n '__fish_git_using_command clone' -l recursive -d 'Initialize all submodules within the cloned repository' ### commit -complete -c git -n '__fish_git_needs_command' -a commit -d 'Record changes to the repository' +complete -c git -n '__fish_git_needs_command' -a commit -d 'Record changes to the repository' complete -c git -n '__fish_git_using_command commit' -l amend -d 'Amend the log message of the last commit' complete -f -c git -n '__fish_git_using_command commit' -a '(__fish_git_modified_files)' complete -f -c git -n '__fish_git_using_command commit' -l fixup -d 'Fixup commit to be used with rebase --autosquash' @@ -345,21 +356,21 @@ complete -f -c git -n '__fish_git_using_command commit; and __fish_contains_opt # TODO options ### diff -complete -c git -n '__fish_git_needs_command' -a diff -d 'Show changes between commits, commit and working tree, etc' +complete -c git -n '__fish_git_needs_command' -a diff -d 'Show changes between commits, commit and working tree, etc' complete -c git -n '__fish_git_using_command diff' -a '(__fish_git_ranges)' -d 'Branch' complete -c git -n '__fish_git_using_command diff' -l cached -d 'Show diff of changes in the index' complete -c git -n '__fish_git_using_command diff' -l no-index -d 'Compare two paths on the filesystem' # TODO options ### difftool -complete -c git -n '__fish_git_needs_command' -a difftool -d 'Open diffs in a visual tool' +complete -c git -n '__fish_git_needs_command' -a difftool -d 'Open diffs in a visual tool' complete -c git -n '__fish_git_using_command difftool' -a '(__fish_git_ranges)' -d 'Branch' complete -c git -n '__fish_git_using_command difftool' -l cached -d 'Visually show diff of changes in the index' # TODO options ### grep -complete -c git -n '__fish_git_needs_command' -a grep -d 'Print lines matching a pattern' +complete -c git -n '__fish_git_needs_command' -a grep -d 'Print lines matching a pattern' # TODO options ### init @@ -367,7 +378,7 @@ complete -f -c git -n '__fish_git_needs_command' -a init -d 'Create an empty git # TODO options ### log -complete -c git -n '__fish_git_needs_command' -a log -d 'Show commit logs' +complete -c git -n '__fish_git_needs_command' -a log -d 'Show commit logs' complete -c git -n '__fish_git_using_command log' -a '(__fish_git_heads) (__fish_git_ranges)' -d 'Branch' # TODO options @@ -398,7 +409,7 @@ complete -f -c git -n '__fish_git_using_command merge' -l abort -d 'Abort the cu # TODO options ### mv -complete -c git -n '__fish_git_needs_command' -a mv -d 'Move or rename a file, a directory, or a symlink' +complete -c git -n '__fish_git_needs_command' -a mv -d 'Move or rename a file, a directory, or a symlink' # TODO options ### prune @@ -466,7 +477,7 @@ complete -f -c git -n '__fish_git_using_command rebase' -l no-autosquash -d 'No complete -f -c git -n '__fish_git_using_command rebase' -l no-ff -d 'No fast-forward' ### reset -complete -c git -n '__fish_git_needs_command' -a reset -d 'Reset current HEAD to the specified state' +complete -c git -n '__fish_git_needs_command' -a reset -d 'Reset current HEAD to the specified state' complete -f -c git -n '__fish_git_using_command reset' -l hard -d 'Reset files in working directory' complete -c git -n '__fish_git_using_command reset' -a '(__fish_git_branches)' -d 'Branch' complete -f -c git -n '__fish_git_using_command reset' -a '(__fish_git_staged_files)' -d 'File' @@ -478,7 +489,7 @@ complete -f -c git -n '__fish_git_using_command revert' -a '(__fish_git_commits) # TODO options ### rm -complete -c git -n '__fish_git_needs_command' -a rm -d 'Remove files from the working tree and from the index' +complete -c git -n '__fish_git_needs_command' -a rm -d 'Remove files from the working tree and from the index' complete -c git -n '__fish_git_using_command rm' -f complete -c git -n '__fish_git_using_command rm' -l cached -d 'Keep local copies' complete -c git -n '__fish_git_using_command rm' -l ignore-unmatch -d 'Exit with a zero status even if no files matched' @@ -492,7 +503,7 @@ complete -c git -n '__fish_git_using_command rm' -s n -l dry-run -d 'Dry run' complete -f -c git -n '__fish_git_needs_command' -a status -d 'Show the working tree status' complete -f -c git -n '__fish_git_using_command status' -s s -l short -d 'Give the output in the short-format' complete -f -c git -n '__fish_git_using_command status' -s b -l branch -d 'Show the branch and tracking info even in short-format' -complete -f -c git -n '__fish_git_using_command status' -l porcelain -d 'Give the output in a stable, easy-to-parse format' +complete -f -c git -n '__fish_git_using_command status' -l porcelain -d 'Give the output in a stable, easy-to-parse format' complete -f -c git -n '__fish_git_using_command status' -s z -d 'Terminate entries with null character' complete -f -c git -n '__fish_git_using_command status' -s u -l untracked-files -x -a 'no normal all' -d 'The untracked files handling mode' complete -f -c git -n '__fish_git_using_command status' -l ignore-submodules -x -a 'none untracked dirty all' -d 'Ignore changes to submodules' From 3daccf3c22c63d9480df563d1cddd72f9ae3b221 Mon Sep 17 00:00:00 2001 From: CoolOppo Date: Wed, 11 May 2016 18:07:46 -0400 Subject: [PATCH 253/363] fix typo in webconfig.py comment --- share/tools/web_config/webconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/tools/web_config/webconfig.py b/share/tools/web_config/webconfig.py index ee517440b..782751705 100755 --- a/share/tools/web_config/webconfig.py +++ b/share/tools/web_config/webconfig.py @@ -893,7 +893,7 @@ fish_bin_path = None if not fish_bin_dir: print('The __fish_bin_dir environment variable is not set. Looking in $PATH...') # distutils.spawn is terribly broken, because it looks in wd before PATH, - # and doesn't actually validate that the file is even executabl + # and doesn't actually validate that the file is even executable for p in os.environ['PATH'].split(os.pathsep): proposed_path = os.path.join(p, 'fish') if os.access(proposed_path, os.X_OK): From a998921f399f9657cf04313cc99e5116c6d11c98 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 13 May 2016 16:18:29 +0200 Subject: [PATCH 254/363] git: Complete reflog for reset --- share/completions/git.fish | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/share/completions/git.fish b/share/completions/git.fish index 234db0d7c..1bdec5cc0 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -199,6 +199,10 @@ function __fish_git_possible_commithash return 1 end +function __fish_git_reflog + command git reflog | string replace -r '[0-9a-f]* (.+@\{[0-9]+\}): (.*)$' '$1\t$2' +end + # general options complete -f -c git -l help -d 'Display the manual of a git command' complete -f -c git -n '__fish_git_using_command log show diff-tree rev-list' -l pretty -a 'oneline short medium full fuller email raw format:' @@ -481,6 +485,7 @@ complete -c git -n '__fish_git_needs_command' -a reset -d 'Reset current HEAD to complete -f -c git -n '__fish_git_using_command reset' -l hard -d 'Reset files in working directory' complete -c git -n '__fish_git_using_command reset' -a '(__fish_git_branches)' -d 'Branch' complete -f -c git -n '__fish_git_using_command reset' -a '(__fish_git_staged_files)' -d 'File' +complete -f -c git -n '__fish_git_using_command reset' -a '(__fish_git_reflog)' -d 'Reflog' # TODO options ### revert From 149e601743f2ad85df8a3bd0cc857bfd0387ef67 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Fri, 13 May 2016 12:10:17 -0700 Subject: [PATCH 255/363] Remove the errant newline in __fish_cancel_commandline again --- share/functions/__fish_cancel_commandline.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/functions/__fish_cancel_commandline.fish b/share/functions/__fish_cancel_commandline.fish index fb378f250..e48d32ad8 100644 --- a/share/functions/__fish_cancel_commandline.fish +++ b/share/functions/__fish_cancel_commandline.fish @@ -9,7 +9,7 @@ function __fish_cancel_commandline # # Set reverse fg/bg color mode, output ^C, restore normal mode, clear to EOL (to erase any # autosuggestion). - echo (tput smso)"^C"(tput rmso)(tput el) + echo -n (tput smso)"^C"(tput rmso)(tput el) for i in (seq (commandline -L)) echo "" end From 768277a312e1f91248d00d80af814d2c6721dc85 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Fri, 13 May 2016 12:10:17 -0700 Subject: [PATCH 256/363] Remove the errant newline in __fish_cancel_commandline again (cherry picked from commit 149e601743f2ad85df8a3bd0cc857bfd0387ef67) --- share/functions/__fish_cancel_commandline.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/functions/__fish_cancel_commandline.fish b/share/functions/__fish_cancel_commandline.fish index fb378f250..e48d32ad8 100644 --- a/share/functions/__fish_cancel_commandline.fish +++ b/share/functions/__fish_cancel_commandline.fish @@ -9,7 +9,7 @@ function __fish_cancel_commandline # # Set reverse fg/bg color mode, output ^C, restore normal mode, clear to EOL (to erase any # autosuggestion). - echo (tput smso)"^C"(tput rmso)(tput el) + echo -n (tput smso)"^C"(tput rmso)(tput el) for i in (seq (commandline -L)) echo "" end From ff1d651415a2752e82ec417294f9bdf8c234c10f Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sat, 14 May 2016 20:35:54 -0700 Subject: [PATCH 257/363] rename get_is_interactive and remove stupid test I'm doing this as part of fixing issue #2980. The code for managing tty modes and job control is a horrible mess. This is a very tiny step towards improving the situation. --- src/builtin.cpp | 6 +++--- src/parse_execution.cpp | 6 +++--- src/parse_tree.cpp | 2 +- src/parser.cpp | 6 +++--- src/proc.cpp | 7 ++++--- src/proc.h | 2 +- src/reader.cpp | 6 +++--- src/sanity.cpp | 3 +-- src/signal.cpp | 8 +++----- 9 files changed, 22 insertions(+), 24 deletions(-) diff --git a/src/builtin.cpp b/src/builtin.cpp index db528aa0e..e5261cb48 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -181,7 +181,7 @@ void builtin_print_help(parser_t &parser, io_streams_t &streams, const wchar_t * screen_height = common_get_height(); lines = count_char(str, L'\n'); - if (!get_is_interactive() || (lines > 2 * screen_height / 3)) { + if (!shell_is_interactive() || (lines > 2 * screen_height / 3)) { wchar_t *pos; int cut = 0; int i; @@ -2368,7 +2368,7 @@ static int builtin_cd(parser_t &parser, io_streams_t &streams, wchar_t **argv) { argv[0], dir_in.c_str()); } - if (!get_is_interactive()) { + if (!shell_is_interactive()) { streams.err.append(parser.current_line()); } @@ -2385,7 +2385,7 @@ static int builtin_cd(parser_t &parser, io_streams_t &streams, wchar_t **argv) { streams.err.append_format(_(L"%ls: '%ls' is not a directory\n"), argv[0], dir.c_str()); } - if (!get_is_interactive()) { + if (!shell_is_interactive()) { streams.err.append(parser.current_line()); } diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp index 13e499244..cca0b8d9d 100644 --- a/src/parse_execution.cpp +++ b/src/parse_execution.cpp @@ -1183,7 +1183,7 @@ parse_execution_result_t parse_execution_context_t::run_1_job(const parse_node_t // Get terminal modes. struct termios tmodes = {}; - if (get_is_interactive()) { + if (shell_is_interactive()) { if (tcgetattr(STDIN_FILENO, &tmodes)) { // Need real error handling here. wperror(L"tcgetattr"); @@ -1254,14 +1254,14 @@ parse_execution_result_t parse_execution_context_t::run_1_job(const parse_node_t j->tmodes = tmodes; job_set_flag(j, JOB_CONTROL, (job_control_mode == JOB_CONTROL_ALL) || - ((job_control_mode == JOB_CONTROL_INTERACTIVE) && (get_is_interactive()))); + ((job_control_mode == JOB_CONTROL_INTERACTIVE) && (shell_is_interactive()))); job_set_flag(j, JOB_FOREGROUND, !tree.job_should_be_backgrounded(job_node)); 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()); + is_subshell || is_block || is_event || !shell_is_interactive()); // Tell the current block what its job is. This has to happen before we populate it (#1394). parser->current_block()->job = j; diff --git a/src/parse_tree.cpp b/src/parse_tree.cpp index 1ec7aa439..8bd7d4f9a 100644 --- a/src/parse_tree.cpp +++ b/src/parse_tree.cpp @@ -139,7 +139,7 @@ wcstring parse_error_t::describe_with_prefix(const wcstring &src, const wcstring } wcstring parse_error_t::describe(const wcstring &src) const { - return this->describe_with_prefix(src, wcstring(), get_is_interactive(), false); + return this->describe_with_prefix(src, wcstring(), shell_is_interactive(), false); } void parse_error_offset_source_start(parse_error_list_t *errors, size_t amt) { diff --git a/src/parser.cpp b/src/parser.cpp index 47d1994f0..17d845abb 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -522,7 +522,7 @@ wcstring parser_t::current_line() { wcstring prefix; // If we are not going to print a stack trace, at least print the line number and filename. - if (!get_is_interactive() || is_function()) { + if (!shell_is_interactive() || is_function()) { if (file) { append_format(prefix, _(L"%ls (line %d): "), user_presentable_path(file).c_str(), lineno); @@ -533,7 +533,7 @@ wcstring parser_t::current_line() { } } - bool is_interactive = get_is_interactive(); + bool is_interactive = shell_is_interactive(); bool skip_caret = is_interactive && !is_function(); // Use an error with empty text. @@ -756,7 +756,7 @@ void parser_t::get_backtrace(const wcstring &src, const parse_error_list_t &erro if (!errors.empty()) { const parse_error_t &err = errors.at(0); - const bool is_interactive = get_is_interactive(); + const bool is_interactive = shell_is_interactive(); // Determine if we want to try to print a caret to point at the source error. The // err.source_start <= src.size() check is due to the nasty way that slices work, which is diff --git a/src/proc.cpp b/src/proc.cpp index 305be0df9..2e3707543 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -101,10 +101,11 @@ static int is_interactive = -1; static bool proc_had_barrier = false; -int get_is_interactive(void) { +bool shell_is_interactive(void) { ASSERT_IS_MAIN_THREAD(); - // is_interactive is initialized to -1; ensure someone has popped/pushed it before then. - assert(is_interactive >= 0); + // is_interactive is statically initialized to -1. Ensure it has been dynamically set + // before we're called. + assert(is_interactive != -1); return is_interactive > 0; } diff --git a/src/proc.h b/src/proc.h index 3d24405ca..704f87195 100644 --- a/src/proc.h +++ b/src/proc.h @@ -232,7 +232,7 @@ extern int is_subshell; extern int is_block; /// Whether we are reading from the keyboard right now. -int get_is_interactive(void); +bool shell_is_interactive(void); /// Whether this shell is attached to the keyboard at all. extern int is_interactive_session; diff --git a/src/reader.cpp b/src/reader.cpp index bd4288711..235086c9c 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -1686,7 +1686,7 @@ static void reader_interactive_destroy() { void reader_sanity_check() { // Note: 'data' is non-null if we are interactive, except in the testing environment. - if (get_is_interactive() && data != NULL) { + if (shell_is_interactive() && data != NULL) { if (data->command_line.position > data->command_line.size()) sanity_lose(); if (data->colors.size() != data->command_line.size()) sanity_lose(); if (data->indents.size() != data->command_line.size()) sanity_lose(); @@ -2217,7 +2217,7 @@ static void reader_super_highlight_me_plenty(int match_highlight_pos_adjust, boo } bool shell_is_exiting() { - if (get_is_interactive()) + if (shell_is_interactive()) return job_list_is_empty() && data != NULL && data->end_loop; else return end_loop; @@ -3412,7 +3412,7 @@ int reader_read(int fd, const io_chain_t &io) { int inter = ((fd == STDIN_FILENO) && isatty(STDIN_FILENO)); proc_push_interactive(inter); - res = get_is_interactive() ? read_i() : read_ni(fd, io); + res = shell_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; diff --git a/src/sanity.cpp b/src/sanity.cpp index d6da40f46..895112ca4 100644 --- a/src/sanity.cpp +++ b/src/sanity.cpp @@ -18,8 +18,7 @@ void sanity_lose() { } int sanity_check() { - if (!insane) - if (get_is_interactive()) history_sanity_check(); + if (!insane && shell_is_interactive()) history_sanity_check(); if (!insane) reader_sanity_check(); if (!insane) kill_sanity_check(); if (!insane) proc_sanity_check(); diff --git a/src/signal.cpp b/src/signal.cpp index f5c356f7e..f232b01c5 100644 --- a/src/signal.cpp +++ b/src/signal.cpp @@ -249,8 +249,6 @@ void signal_reset_handlers() { void signal_set_handlers() { struct sigaction act; - if (get_is_interactive() == -1) return; - sigemptyset(&act.sa_mask); act.sa_flags = SA_SIGINFO; act.sa_sigaction = &default_handler; @@ -267,9 +265,9 @@ void signal_set_handlers() { // Ignore sigpipe, which we may get from the universal variable notifier. sigaction(SIGPIPE, &act, 0); - if (get_is_interactive()) { - // Interactive mode. Ignore interactive signals. We are a shell, we know whats best for the - // user. + if (shell_is_interactive()) { + // Interactive mode. Ignore interactive signals. We are a shell, we know what is best for + // the user. act.sa_handler = SIG_IGN; sigaction(SIGINT, &act, 0); From 51468b764689e7d724a87e6c2b8cdb4e599a3604 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sun, 8 May 2016 15:57:56 -0700 Subject: [PATCH 258/363] add `function --shadow-builtin` flag It's currently too easy for someone to bork their shell by doing something like `function test; return 0; end`. That's obviously a silly, contrived, example but the point is that novice users who learn about functions are prone to do something like that without realizing it will bork the shell. Even expert users who know about the `test` builtin might forget that, say, `pwd` is a builtin. This change adds a `--shadow-builtin` flag that must be specified to indicate you know what you're doing. Fixes #3000 --- doc_src/function.txt | 2 + .../functions/__fish_config_interactive.fish | 18 ++---- share/functions/cd.fish | 2 +- share/functions/history.fish | 27 ++++----- src/builtin.cpp | 55 ++++++++++++++---- src/builtin.h | 8 +-- src/exec.cpp | 4 +- src/function.cpp | 16 ++++-- src/function.h | 56 +++++++++---------- src/parse_execution.cpp | 4 +- tests/function.err | 8 +++ tests/function.in | 13 +++++ tests/function.out | 1 + 13 files changed, 133 insertions(+), 81 deletions(-) diff --git a/doc_src/function.txt b/doc_src/function.txt index e58d6513a..65cbdca98 100644 --- a/doc_src/function.txt +++ b/doc_src/function.txt @@ -29,6 +29,8 @@ The following options are available: - `-s` or `--on-signal SIGSPEC` tells fish to run this function when the signal SIGSPEC is delivered. SIGSPEC can be a signal number, or the signal name, such as SIGHUP (or just HUP). +- `-B` or `--shadow-builtin` must be specified if the function name is the same as a builtin. Specifying this flag indicates your acknowledgement that you are wrapping or replacing the builtin command. This is a safety feature to make it harder for people to inadvertently break the shell by doing things like `function test; return 0; end`. If the function name is not currently a builtin using this flag will produce an error. If you want to write a function that provides a builtin to an older version of fish you need to add something like `builtin --names | grep -q '^cmd$'; and return` to the top of the function script (where `cmd` is the name of the builtin/function). That will keep your script from replacing the builtin with your function on the newer fish version while allowing your function to provide similar functionality on older versions of fish. + - `-S` or `--no-scope-shadowing` allows the function to access the variables of calling functions. Normally, any variables inside the function that have the same name as variables from the calling function are "shadowed", and their contents is independent of the calling function. - `-V` or `--inherit-variable NAME` snapshots the value of the variable `NAME` and defines a local variable with that same name and value when the function is executed. diff --git a/share/functions/__fish_config_interactive.fish b/share/functions/__fish_config_interactive.fish index 7b306e3d5..ec891ddd2 100644 --- a/share/functions/__fish_config_interactive.fish +++ b/share/functions/__fish_config_interactive.fish @@ -4,9 +4,7 @@ # This function is called by the __fish_on_interactive function, which is defined in config.fish. # function __fish_config_interactive -d "Initializations that should be performed when entering interactive mode" - - - # Make sure this function is only run once + # Make sure this function is only run once. if set -q __fish_config_interactive_done return end @@ -27,7 +25,6 @@ function __fish_config_interactive -d "Initializations that should be performed # # If we are starting up for the first time, set various defaults # - if not set -q __fish_init_1_50_0 if not set -q fish_greeting set -l line1 (printf (_ 'Welcome to fish, the friendly interactive shell') ) @@ -97,25 +94,22 @@ function __fish_config_interactive -d "Initializations that should be performed # # Directory history colors # - set -q fish_color_history_current or set -U fish_color_history_current cyan end # - # Generate man page completions if not present + # Generate man page completions if not present. # - if not test -d $userdatadir/fish/generated_completions #fish_update_completions is a function, so it can not be directly run in background. eval "$__fish_bin_dir/fish -c 'fish_update_completions > /dev/null ^/dev/null' &" end # - # Print a greeting - # fish_greeting can be a function (preferred) or a variable + # Print a greeting. + # fish_greeting can be a function (preferred) or a variable. # - if functions -q fish_greeting fish_greeting else @@ -130,7 +124,6 @@ function __fish_config_interactive -d "Initializations that should be performed # fish_color_cwd changes value. Like all event handlers, it can't be # autoloaded. # - function __fish_repaint --on-variable fish_color_cwd --description "Event handler, repaints the prompt when fish_color_cwd changes" if status --is-interactive set -e __fish_prompt_cwd @@ -149,7 +142,6 @@ function __fish_config_interactive -d "Initializations that should be performed # Completions for SysV startup scripts. These aren't bound to any # specific command, so they can't be autoloaded. # - complete -x -p "/etc/init.d/*" -a start --description 'Start service' complete -x -p "/etc/init.d/*" -a stop --description 'Stop service' complete -x -p "/etc/init.d/*" -a status --description 'Print service status' @@ -293,7 +285,7 @@ function __fish_config_interactive -d "Initializations that should be performed # Don't allow setting color other than what linux offers (see #2001) functions -e set_color - function set_color + function set_color --shadow-builtin set -l term_colors black red green yellow blue magenta cyan white normal for a in $argv if not contains -- $a $term_colors diff --git a/share/functions/cd.fish b/share/functions/cd.fish index adf0327ca..fdc8b607f 100644 --- a/share/functions/cd.fish +++ b/share/functions/cd.fish @@ -1,7 +1,7 @@ # # Wrap the builtin cd command to maintain directory history. # -function cd --description "Change directory" +function cd --shadow-builtin --description "Change directory" set -l MAX_DIR_HIST 25 if test (count $argv) -gt 1 diff --git a/share/functions/history.fish b/share/functions/history.fish index 0de39e30b..a365ae19e 100644 --- a/share/functions/history.fish +++ b/share/functions/history.fish @@ -1,16 +1,12 @@ # -#Deletes an item from history +# Wrap the builtin history command to provide additional functionality. # -function history --description "Deletes an item from history" - +function history --shadow-builtin --description "Deletes an item from history" set -l argc (count $argv) set -l prefix_args "" set -l contains_args "" - set -l cmd print - set -l search_mode none - set -l pager less if set -q PAGER set pager $PAGER @@ -46,7 +42,7 @@ function history --description "Deletes an item from history" end end else - #Execute history builtin without any argument + # Execute history builtin without any argument. if status --is-interactive builtin history | eval $pager else @@ -57,9 +53,8 @@ function history --description "Deletes an item from history" switch $cmd case print - # Print matching items - # Note this may end up passing --search twice to the builtin, - # but that's harmless + # Print matching items. Note this may end up passing --search twice to the builtin, + # but that's harmless. builtin history --search $argv case delete @@ -72,8 +67,7 @@ function history --description "Deletes an item from history" set found_items (builtin history --search --contains $contains_args) case none builtin history $argv - - #Save changes after deleting item + # Save changes after deleting item. builtin history --save return 0 end @@ -98,7 +92,8 @@ function history --description "Deletes an item from history" continue end - #Following two validations could be embedded with "and" but I find the syntax kind of weird. + # Following two validations could be embedded with "and" but I find the syntax + # kind of weird. if not string match -qr '^[0-9]+$' $i printf "Invalid input: %s\n" $i continue @@ -124,18 +119,18 @@ function history --description "Deletes an item from history" end end - #Save changes after deleting item(s) + # Save changes after deleting item(s). builtin history --save end case save - #Save changes to history file + # Save changes to history file. builtin history $argv case merge builtin history --merge case help builtin history --help case clear - # Erase the entire history + # Erase the entire history. echo "Are you sure you want to clear history ? (y/n)" read ch if test $ch = "y" diff --git a/src/builtin.cpp b/src/builtin.cpp index e5261cb48..a79f898d8 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -889,6 +889,7 @@ static int builtin_generic(parser_t &parser, io_streams_t &streams, wchar_t **ar } } } + return STATUS_BUILTIN_ERROR; } @@ -919,7 +920,11 @@ static wcstring functions_def(const wcstring &name) { out.append(esc_desc); } - if (!function_get_shadows(name)) { + if (function_get_shadow_builtin(name)) { + out.append(L" --shadow-builtin"); + } + + if (!function_get_shadow_scope(name)) { out.append(L" --no-scope-shadowing"); } @@ -1460,8 +1465,8 @@ static int builtin_pwd(parser_t &parser, io_streams_t &streams, wchar_t **argv) } /// Adds a function to the function set. It calls into function.cpp to perform any heavy lifting. -int define_function(parser_t &parser, io_streams_t &streams, const wcstring_list_t &c_args, - const wcstring &contents, int definition_line_offset, wcstring *out_err) { +int builtin_function(parser_t &parser, io_streams_t &streams, const wcstring_list_t &c_args, + const wcstring &contents, int definition_line_offset, wcstring *out_err) { wgetopter_t w; assert(out_err != NULL); @@ -1484,7 +1489,8 @@ int define_function(parser_t &parser, io_streams_t &streams, const wcstring_list wcstring_list_t named_arguments; wcstring_list_t inherit_vars; - bool shadows = true; + bool shadow_builtin = false; + bool shadow_scope = true; wcstring_list_t wrap_targets; @@ -1504,17 +1510,17 @@ int define_function(parser_t &parser, io_streams_t &streams, const wcstring_list {L"wraps", required_argument, 0, 'w'}, {L"help", no_argument, 0, 'h'}, {L"argument-names", no_argument, 0, 'a'}, + {L"shadow-builtin", no_argument, 0, 'B'}, {L"no-scope-shadowing", no_argument, 0, 'S'}, {L"inherit-variable", required_argument, 0, 'V'}, {0, 0, 0, 0}}; - while (1 && (!res)) { + while (1 && !res) { int opt_index = 0; // The leading - here specifies RETURN_IN_ORDER. - int opt = w.wgetopt_long(argc, argv, L"-d:s:j:p:v:e:w:haSV:", long_options, &opt_index); + int opt = w.wgetopt_long(argc, argv, L"-d:s:j:p:v:e:w:haBSV:", long_options, &opt_index); if (opt == -1) break; - switch (opt) { case 0: { if (long_options[opt_index].flag != 0) break; @@ -1536,7 +1542,6 @@ int define_function(parser_t &parser, io_streams_t &streams, const wcstring_list events.push_back(event_t::signal_event(sig)); break; } - case 'v': { if (wcsvarname(w.woptarg)) { append_format(*out_err, _(L"%ls: Invalid variable name '%ls'"), argv[0], @@ -1586,7 +1591,6 @@ int define_function(parser_t &parser, io_streams_t &streams, const wcstring_list e.type = EVENT_JOB_ID; e.param1.job_id = job_id; } - } else { errno = 0; pid = fish_wcstoi(w.woptarg, &end, 10); @@ -1611,8 +1615,12 @@ int define_function(parser_t &parser, io_streams_t &streams, const wcstring_list name_is_first_positional = !positionals.empty(); break; } + case 'B': { + shadow_builtin = true; + break; + } case 'S': { - shadows = 0; + shadow_scope = false; break; } case 'w': { @@ -1699,6 +1707,30 @@ int define_function(parser_t &parser, io_streams_t &streams, const wcstring_list } } + if (!res) { + bool function_name_shadows_builtin = false; + wcstring_list_t builtin_names = builtin_get_names(); + for (size_t i = 0; i < builtin_names.size(); i++) { + const wchar_t *el = builtin_names.at(i).c_str(); + if (el == function_name) { + function_name_shadows_builtin = true; + break; + } + } + if (function_name_shadows_builtin && !shadow_builtin) { + append_format( + *out_err, + _(L"%ls: function name shadows a builtin so you must use '--shadow-builtin'"), + argv[0]); + res = STATUS_BUILTIN_ERROR; + } else if (!function_name_shadows_builtin && shadow_builtin) { + append_format(*out_err, _(L"%ls: function name does not shadow a builtin so you " + L"must not use '--shadow-builtin'"), + argv[0]); + res = STATUS_BUILTIN_ERROR; + } + } + if (!res) { // Here we actually define the function! function_data_t d; @@ -1706,7 +1738,8 @@ int define_function(parser_t &parser, io_streams_t &streams, const wcstring_list d.name = function_name; if (desc) d.description = desc; d.events.swap(events); - d.shadows = shadows; + d.shadow_builtin = shadow_builtin; + d.shadow_scope = shadow_scope; d.named_arguments.swap(named_arguments); d.inherit_vars.swap(inherit_vars); diff --git a/src/builtin.h b/src/builtin.h index 0d62babe5..7648649d7 100644 --- a/src/builtin.h +++ b/src/builtin.h @@ -93,10 +93,10 @@ class builtin_commandline_scoped_transient_t { // Run the __fish_print_help function to obtain the help information for the specified command. wcstring builtin_help_get(parser_t &parser, const wchar_t *cmd); -// Defines a function, like builtin_function. Returns 0 on success. args should NOT contain -// 'function' as the first argument. -int define_function(parser_t &parser, io_streams_t &streams, const wcstring_list_t &c_args, - const wcstring &contents, int definition_line_offset, wcstring *out_err); +// Defines a function. Returns 0 on success. args should NOT contain 'function' as the first +// argument as the parser treats it as a keyword. +int builtin_function(parser_t &parser, io_streams_t &streams, const wcstring_list_t &c_args, + const wcstring &contents, int definition_line_offset, wcstring *out_err); // Print help for the specified builtin. If \c b is sb_err, also print the line information. void builtin_print_help(parser_t &parser, io_streams_t &streams, const wchar_t *cmd, diff --git a/src/exec.cpp b/src/exec.cpp index 24a9af05c..63f2cb775 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -636,7 +636,7 @@ void exec_job(parser_t &parser, job_t *j) { wcstring def; bool function_exists = function_get_definition(func_name, &def); - bool shadows = function_get_shadows(func_name); + bool shadow_scope = function_get_shadow_scope(func_name); const std::map inherit_vars = function_get_inherit_vars(func_name); @@ -646,7 +646,7 @@ void exec_job(parser_t &parser, job_t *j) { debug(0, _(L"Unknown function '%ls'"), p->argv0()); break; } - function_block_t *newv = new function_block_t(p, func_name, shadows); + function_block_t *newv = new function_block_t(p, func_name, shadow_scope); parser.push_block(newv); // Setting variables might trigger an event handler, hence we need to unblock diff --git a/src/function.cpp b/src/function.cpp index f717a16fe..54559beee 100644 --- a/src/function.cpp +++ b/src/function.cpp @@ -141,7 +141,8 @@ function_info_t::function_info_t(const function_data_t &data, const wchar_t *fil named_arguments(data.named_arguments), inherit_vars(snapshot_vars(data.inherit_vars)), is_autoload(autoload), - shadows(data.shadows) {} + shadow_builtin(data.shadow_builtin), + shadow_scope(data.shadow_scope) {} function_info_t::function_info_t(const function_info_t &data, const wchar_t *filename, int def_offset, bool autoload) @@ -152,7 +153,8 @@ function_info_t::function_info_t(const function_info_t &data, const wchar_t *fil named_arguments(data.named_arguments), inherit_vars(data.inherit_vars), is_autoload(autoload), - shadows(data.shadows) {} + shadow_builtin(data.shadow_builtin), + shadow_scope(data.shadow_scope) {} void function_add(const function_data_t &data, const parser_t &parser, int definition_line_offset) { ASSERT_IS_MAIN_THREAD(); @@ -255,10 +257,16 @@ std::map function_get_inherit_vars(const wcstring &name) { return func ? func->inherit_vars : std::map(); } -int function_get_shadows(const wcstring &name) { +int function_get_shadow_builtin(const wcstring &name) { scoped_lock lock(functions_lock); const function_info_t *func = function_get(name); - return func ? func->shadows : false; + return func ? func->shadow_builtin : false; +} + +int function_get_shadow_scope(const wcstring &name) { + scoped_lock lock(functions_lock); + const function_info_t *func = function_get(name); + return func ? func->shadow_scope : false; } bool function_get_desc(const wcstring &name, wcstring *out_desc) { diff --git a/src/function.h b/src/function.h index 04829cbe9..59b37051d 100644 --- a/src/function.h +++ b/src/function.h @@ -31,12 +31,34 @@ struct function_data_t { /// List of all variables that are inherited from the function definition scope. The variable /// values are snapshotted when function_add() is called. wcstring_list_t inherit_vars; - /// Set to non-zero if invoking this function shadows the variables of the underlying function. - int shadows; + /// Set to true if invoking this function shadows the variables of the underlying function. + bool shadow_scope; + /// Set to true if this function shadows a builtin. + bool shadow_builtin; }; class function_info_t { public: + /// Function definition. + const wcstring definition; + /// Function description. Only the description may be changed after the function is created. + wcstring description; + /// File where this function was defined (intern'd string). + const wchar_t *const definition_file; + /// Line where definition started. + const int definition_offset; + /// List of all named arguments for this function. + const wcstring_list_t named_arguments; + /// Mapping of all variables that were inherited from the function definition scope to their + /// values. + const std::map inherit_vars; + /// Flag for specifying that this function was automatically loaded. + const bool is_autoload; + /// Set to true if this function shadows a builtin. + const bool shadow_builtin; + /// Set to true if invoking this function shadows the variables of the underlying function. + const bool shadow_scope; + /// Constructs relevant information from the function_data. function_info_t(const function_data_t &data, const wchar_t *filename, int def_offset, bool autoload); @@ -44,31 +66,6 @@ class function_info_t { /// Used by function_copy. function_info_t(const function_info_t &data, const wchar_t *filename, int def_offset, bool autoload); - - /// Function definition. - const wcstring definition; - - /// Function description. Only the description may be changed after the function is created. - wcstring description; - - /// File where this function was defined (intern'd string). - const wchar_t *const definition_file; - - /// Line where definition started. - const int definition_offset; - - /// List of all named arguments for this function. - const wcstring_list_t named_arguments; - - /// Mapping of all variables that were inherited from the function definition scope to their - /// values. - const std::map inherit_vars; - - /// 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; }; /// Initialize function data. @@ -133,8 +130,11 @@ std::map function_get_inherit_vars(const wcstring &name); /// is successful. bool function_copy(const wcstring &name, const wcstring &new_name); +/// Returns whether this function shadows a builtin of the same name. +int function_get_shadow_builtin(const wcstring &name); + /// Returns whether this function shadows variables of the underlying function. -int function_get_shadows(const wcstring &name); +int function_get_shadow_scope(const wcstring &name); /// Prepares the environment for executing a function. void function_prepare_environment(const wcstring &name, const wchar_t *const *argv, diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp index cca0b8d9d..7989597f9 100644 --- a/src/parse_execution.cpp +++ b/src/parse_execution.cpp @@ -392,8 +392,8 @@ parse_execution_result_t parse_execution_context_t::run_function_statement( int definition_line_offset = this->line_offset_of_character_at_offset(contents_start); wcstring error_str; io_streams_t streams; - int err = define_function(*parser, streams, argument_list, contents_str, - definition_line_offset, &error_str); + int err = builtin_function(*parser, streams, argument_list, contents_str, + definition_line_offset, &error_str); proc_set_last_status(err); if (!error_str.empty()) { diff --git a/tests/function.err b/tests/function.err index e69de29bb..72671ac96 100644 --- a/tests/function.err +++ b/tests/function.err @@ -0,0 +1,8 @@ +function: function name shadows a builtin so you must use '--shadow-builtin' +fish: function pwd; end + ^ +yes, it failed as expected +function: function name does not shadow a builtin so you must not use '--shadow-builtin' +fish: function not_builtin --shadow-builtin; end + ^ +yes, it failed as expected diff --git a/tests/function.in b/tests/function.in index 747bbcecb..8202fb139 100644 --- a/tests/function.in +++ b/tests/function.in @@ -44,3 +44,16 @@ for i in (seq 4) echo "Function name$i not found, but should have been" end end + +# Test that we can't define a function that shadows a builtin by accident. +function pwd; end +or echo 'yes, it failed as expected' >&2 + +# Test that we can define a function that shadows a builtin if we use the +# right flag. +function pwd --shadow-builtin; end +and echo '"function pwd --shadow-builtin" worked' + +# Using --shadow-builtin for a non-builtin function name also fails. +function not_builtin --shadow-builtin; end +or echo 'yes, it failed as expected' >&2 diff --git a/tests/function.out b/tests/function.out index 5a3da6195..0d12479ed 100644 --- a/tests/function.out +++ b/tests/function.out @@ -22,3 +22,4 @@ Function name1 found Function name2 found Function name3 found Function name4 found +"function pwd --shadow-builtin" worked From d55113b5b5e242b1ccfd7d8c916c38b7092b0bd3 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Mon, 16 May 2016 21:06:29 -0700 Subject: [PATCH 259/363] trivial fix to fish_tests.cpp Fix a minor bogosity I noticed while building fish on OS X Snow Leopard. It's technically not a bug because only old compilers complain about the original statement but this change makes the one line this changes consistent with the rest of the fish code. --- src/fish_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 8bf58e1e6..8c1866b14 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -3527,7 +3527,7 @@ static void test_highlighting(void) { do_test(expected_colors.size() == text.size()); std::vector colors(text.size()); - highlight_shell(text, colors, 20, NULL, env_vars_snapshot_t()); + highlight_shell(text, colors, 20, NULL, env_vars_snapshot_t::current()); if (expected_colors.size() != colors.size()) { err(L"Color vector has wrong size! Expected %lu, actual %lu", expected_colors.size(), From 73f2992a2e9ef1c96d45d9e90cc5e1f63a0afc92 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sun, 15 May 2016 19:45:02 -0700 Subject: [PATCH 260/363] make debug() output more useful This change does several things. First, and most important, it allows dumping the "n" most recent stack frames on each debug() call. Second, it demangles the C++ symbols. Third, it prepends each debug() message with the debug level. Unrelated to the above I've replaced all `assert(!is_forked_child());` statements with `ASSERT_IS_NOT_FORKED_CHILD()` for consistency. --- configure.ac | 1 + doc_src/fish.txt | 2 + src/common.cpp | 112 ++++++++++++++++++++++++++++++++++------------- src/common.h | 17 +++---- src/exec.cpp | 2 +- src/fish.cpp | 22 ++++++++-- tests/status.err | 2 +- 7 files changed, 114 insertions(+), 44 deletions(-) diff --git a/configure.ac b/configure.ac index 3301b5bf5..c97b8c51c 100644 --- a/configure.ac +++ b/configure.ac @@ -362,6 +362,7 @@ AC_SEARCH_LIBS( pthread_create, pthread, , [AC_MSG_ERROR([Cannot find the pthrea AC_SEARCH_LIBS( setupterm, [ncurses tinfo curses], , [AC_MSG_ERROR([Could not find a curses implementation, needed to build fish. If this is Linux, try running 'sudo apt-get install libncurses5-dev' or 'sudo yum install ncurses-devel'])] ) AC_SEARCH_LIBS( [nan], [m], [AC_DEFINE( [HAVE_NAN], [1], [Define to 1 if you have the nan function])] ) AC_SEARCH_LIBS( [backtrace_symbols_fd], [execinfo] ) +AC_SEARCH_LIBS( [dladdr], [dl] ) if test x$local_gettext != xno; then AC_SEARCH_LIBS( gettext, intl,,) diff --git a/doc_src/fish.txt b/doc_src/fish.txt index bf9c3eb1b..eb4d24baf 100644 --- a/doc_src/fish.txt +++ b/doc_src/fish.txt @@ -25,4 +25,6 @@ The following options are available: - `-v` or `--version` display version and exit +- `-D` or `--debug-stack-frames=DEBUG_LEVEL` specify how many stack frames to display when debug messages are written. The default is zero. A value of 3 or 4 is usually sufficient to gain insight into how a given debug call was reached but you can specify a value up to 128. + The fish exit status is generally the exit status of the last foreground command. If fish is exiting because of a parse error, the exit status is 127. diff --git a/src/common.cpp b/src/common.cpp index 834f7b8b3..aa27c5371 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -2,6 +2,8 @@ #include "config.h" #include +#include +#include #include #include #include @@ -49,12 +51,16 @@ static bool thread_assertions_configured_for_testing = false; wchar_t ellipsis_char; wchar_t omitted_newline_char; - bool g_profiling_active = false; - const wchar_t *program_name; +int debug_level = 1; // default maximum debug output level (errors and warnings) +int debug_stack_frames = 0; // default number of stack frames to show on debug() calls -int debug_level = 1; +/// This allows us to notice when we've forked. +static pid_t initial_pid = 0; + +/// Be able to restore the term's foreground process group. +static pid_t initial_foreground_process_group = -1; /// This struct maintains the current state of the terminal size. It is updated on demand after /// receiving a SIGWINCH. Do not touch this struct directly, it's managed with a rwlock. Use @@ -65,18 +71,53 @@ static rwlock_t termsize_rwlock; static char *wcs2str_internal(const wchar_t *in, char *out); -void show_stackframe() { +// This function produces a stack backtrace with demangled function & method names. It is based on +// https://gist.github.com/fmela/591333 but adapted to the style of the fish project. +static const wcstring_list_t __attribute__((noinline)) +demangled_backtrace(int max_frames, int skip_levels) { + void *callstack[128]; + const int n_max_frames = sizeof(callstack) / sizeof(callstack[0]); + int n_frames = backtrace(callstack, n_max_frames); + char **symbols = backtrace_symbols(callstack, n_frames); + wchar_t text[1024]; + std::vector backtrace_text; + + if (skip_levels + max_frames < n_frames) n_frames = skip_levels + max_frames; + + for (int i = skip_levels; i < n_frames; i++) { + Dl_info info; + if (dladdr(callstack[i], &info) && info.dli_sname) { + char *demangled = NULL; + int status = -1; + if (info.dli_sname[0] == '_') + demangled = abi::__cxa_demangle(info.dli_sname, NULL, 0, &status); + swprintf(text, sizeof(text) / sizeof(wchar_t), L"%-3d %s + %td", i - skip_levels, + status == 0 ? demangled : info.dli_sname == 0 ? symbols[i] : info.dli_sname, + (char *)callstack[i] - (char *)info.dli_saddr); + free(demangled); + } else { + swprintf(text, sizeof(text) / sizeof(wchar_t), L"%-3d %s", i - skip_levels, symbols[i]); + } + backtrace_text.push_back(text); + } + free(symbols); + return backtrace_text; +} + +static void debug_shared(const wchar_t msg_level, const wcstring &msg); +void __attribute__((noinline)) show_stackframe(const wchar_t msg_level, int frame_count, + int skip_levels) { ASSERT_IS_NOT_FORKED_CHILD(); // Hack to avoid showing backtraces in the tester. if (program_name && !wcscmp(program_name, L"(ignore)")) return; - void *trace[32]; - int trace_size = 0; - - trace_size = backtrace(trace, 32); - debug(0, L"Backtrace:"); - backtrace_symbols_fd(trace, trace_size, STDERR_FILENO); + if (frame_count < 1) frame_count = 999; + debug_shared(msg_level, L"Backtrace:"); + std::vector bt = demangled_backtrace(frame_count, skip_levels + 2); + for (int i = 0; i < bt.size(); i++) { + debug_shared(msg_level, bt[i]); + } } int fgetws2(wcstring *s, FILE *f) { @@ -493,23 +534,34 @@ static bool should_debug(int level) { return true; } -static void debug_shared(const wcstring &msg) { - const wcstring sb = wcstring(program_name) + L": " + msg; - fwprintf(stderr, L"%ls", reformat_for_screen(sb).c_str()); +static void debug_shared(const wchar_t level, const wcstring &msg) { + pid_t current_pid = getpid(); + + if (current_pid == initial_pid) { + fwprintf(stderr, L"<%lc> %ls: %ls\n", (unsigned long)level, program_name, msg.c_str()); + } else { + fwprintf(stderr, L"<%lc> %ls: %d: %ls\n", (unsigned long)level, program_name, current_pid, + msg.c_str()); + } } -void debug(int level, const wchar_t *msg, ...) { +static wchar_t level_char[] = {L'E', L'W', L'2', L'3', L'4', L'5'}; +void __attribute__((noinline)) debug(int level, const wchar_t *msg, ...) { if (!should_debug(level)) return; int errno_old = errno; va_list va; va_start(va, msg); wcstring local_msg = vformat_string(msg, va); va_end(va); - debug_shared(local_msg); + const wchar_t msg_level = level <= 5 ? level_char[level] : L'9'; + debug_shared(msg_level, local_msg); + if (debug_stack_frames > 0) { + show_stackframe(msg_level, debug_stack_frames, 1); + } errno = errno_old; } -void debug(int level, const char *msg, ...) { +void __attribute__((noinline)) debug(int level, const char *msg, ...) { if (!should_debug(level)) return; int errno_old = errno; char local_msg[512]; @@ -517,7 +569,11 @@ void debug(int level, const char *msg, ...) { va_start(va, msg); vsnprintf(local_msg, sizeof local_msg, msg, va); va_end(va); - debug_shared(str2wcstring(local_msg)); + const wchar_t msg_level = level <= 5 ? level_char[level] : L'9'; + debug_shared(msg_level, str2wcstring(local_msg)); + if (debug_stack_frames > 0) { + show_stackframe(msg_level, debug_stack_frames, 1); + } errno = errno_old; } @@ -1470,7 +1526,7 @@ int create_directory(const wcstring &d) { } __attribute__((noinline)) void bugreport() { - debug(1, _(L"This is a bug. Break on bugreport to debug." + 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); } @@ -1630,12 +1686,6 @@ void configure_thread_assertions_for_testing(void) { thread_assertions_configured_for_testing = true; } -/// Notice when we've forked. -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) { // Just bail if nobody's called setup_fork_guards, e.g. some of our tools. if (!initial_pid) return false; @@ -1712,14 +1762,14 @@ void assert_is_locked(void *vmutex, const char *who, const char *caller) { void scoped_lock::lock(void) { assert(!locked); - assert(!is_forked_child()); + ASSERT_IS_NOT_FORKED_CHILD(); VOMIT_ON_FAILURE_NO_ERRNO(pthread_mutex_lock(lock_obj)); locked = true; } void scoped_lock::unlock(void) { assert(locked); - assert(!is_forked_child()); + ASSERT_IS_NOT_FORKED_CHILD(); VOMIT_ON_FAILURE_NO_ERRNO(pthread_mutex_unlock(lock_obj)); locked = false; } @@ -1736,35 +1786,35 @@ scoped_lock::~scoped_lock() { void scoped_rwlock::lock(void) { assert(!(locked || locked_shared)); - assert(!is_forked_child()); + ASSERT_IS_NOT_FORKED_CHILD(); VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_rdlock(rwlock_obj)); locked = true; } void scoped_rwlock::unlock(void) { assert(locked); - assert(!is_forked_child()); + ASSERT_IS_NOT_FORKED_CHILD(); VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_unlock(rwlock_obj)); locked = false; } void scoped_rwlock::lock_shared(void) { assert(!(locked || locked_shared)); - assert(!is_forked_child()); + ASSERT_IS_NOT_FORKED_CHILD(); VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_wrlock(rwlock_obj)); locked_shared = true; } void scoped_rwlock::unlock_shared(void) { assert(locked_shared); - assert(!is_forked_child()); + ASSERT_IS_NOT_FORKED_CHILD(); VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_unlock(rwlock_obj)); locked_shared = false; } void scoped_rwlock::upgrade(void) { assert(locked_shared); - assert(!is_forked_child()); + ASSERT_IS_NOT_FORKED_CHILD(); VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_unlock(rwlock_obj)); locked = false; VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_wrlock(rwlock_obj)); diff --git a/src/common.h b/src/common.h index 3d1a771f5..ea0805f02 100644 --- a/src/common.h +++ b/src/common.h @@ -175,6 +175,9 @@ extern wchar_t omitted_newline_char; /// it will not be printed. extern int debug_level; +/// How many stack frames to show when a debug() call is made. +extern int debug_stack_frames; + /// Profiling flag. True if commands should be profiled. extern bool g_profiling_active; @@ -192,7 +195,7 @@ void write_ignore(int fd, const void *buff, size_t count); if (!(arg)) { \ debug(0, "function %s called with null value for argument %s. ", __func__, #arg); \ bugreport(); \ - show_stackframe(); \ + show_stackframe(L'E'); \ return retval; \ } @@ -200,7 +203,7 @@ void write_ignore(int fd, const void *buff, size_t count); #define FATAL_EXIT() \ { \ char exit_read_buff; \ - show_stackframe(); \ + show_stackframe(L'E'); \ read_ignore(0, &exit_read_buff, 1); \ exit_without_destructors(1); \ } @@ -219,7 +222,7 @@ void write_ignore(int fd, const void *buff, size_t count); if (signal_is_blocked()) { \ debug(0, "function %s called while blocking signals. ", __func__); \ bugreport(); \ - show_stackframe(); \ + show_stackframe(L'E'); \ return retval; \ } @@ -234,7 +237,7 @@ void write_ignore(int fd, const void *buff, size_t count); #define contains(str, ...) contains_internal(str, 0, __VA_ARGS__, NULL) /// Print a stack trace to stderr. -void show_stackframe(); +void show_stackframe(const wchar_t msg_level, int frame_count = -1, int skip_levels = 0); /// Read a line from the stream f into the string. Returns the number of bytes read or -1 on /// failure. @@ -485,8 +488,6 @@ class null_terminated_array_t { void convert_wide_array_to_narrow(const null_terminated_array_t &arr, null_terminated_array_t *output); -bool is_forked_child(); - class mutex_lock_t { public: pthread_mutex_t mutex; @@ -668,8 +669,8 @@ 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 __attribute__((noinline)) debug(int level, const char *msg, ...); +void __attribute__((noinline)) debug(int level, const wchar_t *msg, ...); /// Replace special characters with backslash escape sequences. Newline is replaced with \n, etc. /// diff --git a/src/exec.cpp b/src/exec.cpp index 63f2cb775..afd09f6aa 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -948,7 +948,7 @@ void exec_job(parser_t &parser, job_t *j) { bool builtin_io_done = do_builtin_io(outbuff.data(), outbuff.size(), errbuff.data(), errbuff.size()); if (!builtin_io_done && errno != EPIPE) { - show_stackframe(); + show_stackframe(L'E'); } fork_was_skipped = true; } diff --git a/src/fish.cpp b/src/fish.cpp index d8192c86d..c4158dde9 100644 --- a/src/fish.cpp +++ b/src/fish.cpp @@ -309,11 +309,12 @@ static int read_init(const struct config_paths_t &paths) { return 1; } -/// Parse the argument list, return the index of the first non-switch arguments. +/// Parse the argument list, return the index of the first non-flag arguments. static int fish_parse_opt(int argc, char **argv, std::vector *cmds) { - const char *short_opts = "+hilnvc:p:d:"; + const char *short_opts = "+hilnvc:p:d:D:"; const struct option long_opts[] = {{"command", required_argument, NULL, 'c'}, {"debug-level", required_argument, NULL, 'd'}, + {"debug-stack-frames", required_argument, NULL, 'D'}, {"interactive", no_argument, NULL, 'i'}, {"login", no_argument, NULL, 'l'}, {"no-execute", no_argument, NULL, 'n'}, @@ -343,7 +344,7 @@ static int fish_parse_opt(int argc, char **argv, std::vector *cmds) if (tmp >= 0 && tmp <= 10 && !*end && !errno) { debug_level = (int)tmp; } else { - fwprintf(stderr, _(L"Invalid value '%s' for debug level switch"), optarg); + fwprintf(stderr, _(L"Invalid value '%s' for debug-level flag"), optarg); exit(1); } break; @@ -373,6 +374,21 @@ static int fish_parse_opt(int argc, char **argv, std::vector *cmds) fwprintf(stdout, _(L"%s, version %s\n"), PACKAGE_NAME, get_fish_version()); exit(0); } + case 'D': { + char *end; + long tmp; + + errno = 0; + tmp = strtol(optarg, &end, 10); + + if (tmp > 0 && tmp <= 128 && !*end && !errno) { + debug_stack_frames = (int)tmp; + } else { + fwprintf(stderr, _(L"Invalid value '%s' for debug-stack-frames flag"), optarg); + exit(1); + } + break; + } default: { // We assume getopt_long() has already emitted a diagnostic msg. exit(1); diff --git a/tests/status.err b/tests/status.err index 91645c95f..b04178e47 100644 --- a/tests/status.err +++ b/tests/status.err @@ -1,2 +1,2 @@ -fish: An error occurred while redirecting file '/' + fish: An error occurred while redirecting file '/' open: Is a directory From 00e32a0909e5d0d303936eec8cb697cb67069861 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Sat, 9 Apr 2016 22:28:00 -0700 Subject: [PATCH 261/363] Use fonts found on terminals for the web config. Instead of just using Courier New across the board, have the browser try several likely available fonts before defaulting to the system's "monospace". Thanks @MarkGriffiths Fixes #2924 --- share/tools/web_config/fishconfig.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/tools/web_config/fishconfig.css b/share/tools/web_config/fishconfig.css index bba26b41b..b4dcd548c 100644 --- a/share/tools/web_config/fishconfig.css +++ b/share/tools/web_config/fishconfig.css @@ -1,6 +1,6 @@ body { background-color: #292929; - font-family: Courier, "Courier New", monospace; + font-family: "Source Code Pro", "DejaVu Sans Mono", Menlo, "Ubuntu Mono", Consolas, Monaco, "Lucida Console", monospace, fixed; color: white; } From 9225b16d1285e620c7c13e3416bdd4099f8ff4b7 Mon Sep 17 00:00:00 2001 From: David Adam Date: Wed, 18 May 2016 22:30:21 +0000 Subject: [PATCH 262/363] add (or restore) config.h to all files The autoconf-generated config.h contains a number of directives which may alter the behaviour of system headers on certain platforms. Always include it in every C++ file as the first include. Closes #2993. --- src/autoload.cpp | 2 ++ src/builtin.cpp | 1 + src/builtin_commandline.cpp | 2 ++ src/builtin_complete.cpp | 2 ++ src/builtin_printf.cpp | 1 + src/builtin_set.cpp | 2 ++ src/builtin_test.cpp | 2 ++ src/builtin_ulimit.cpp | 2 ++ src/color.cpp | 2 ++ src/complete.cpp | 2 ++ src/env.cpp | 2 ++ src/event.cpp | 2 ++ src/fish_indent.cpp | 2 ++ src/fish_key_reader.cpp | 2 ++ src/fish_tests.cpp | 1 + src/function.cpp | 2 ++ src/highlight.cpp | 2 ++ src/history.cpp | 2 ++ src/intern.cpp | 2 ++ src/io.cpp | 2 ++ src/iothread.cpp | 2 ++ src/kill.cpp | 2 ++ src/pager.cpp | 2 ++ src/parse_execution.cpp | 2 ++ src/parse_productions.cpp | 2 ++ src/parse_tree.cpp | 2 ++ src/parse_util.cpp | 2 ++ src/parser.cpp | 2 ++ src/parser_keywords.cpp | 2 ++ src/path.cpp | 2 ++ src/postfork.cpp | 2 ++ src/print_help.cpp | 2 ++ src/sanity.cpp | 2 ++ src/signal.cpp | 2 ++ src/tokenizer.cpp | 2 ++ src/utf8.cpp | 2 ++ src/util.cpp | 2 ++ src/wcstringutil.cpp | 2 ++ src/wildcard.cpp | 2 ++ 39 files changed, 75 insertions(+) diff --git a/src/autoload.cpp b/src/autoload.cpp index 31f6e6df3..2d404cf9d 100644 --- a/src/autoload.cpp +++ b/src/autoload.cpp @@ -1,4 +1,6 @@ // The classes responsible for autoloading functions and completions. +#include "config.h" // IWYU pragma: keep + #include #include #include diff --git a/src/builtin.cpp b/src/builtin.cpp index a79f898d8..336f09eb5 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -15,6 +15,7 @@ // Check the other builtin manuals for proper syntax. // // 4). Use 'git add doc_src/NAME.txt' to start tracking changes to the documentation file. +#include "config.h" // IWYU pragma: keep #include #include diff --git a/src/builtin_commandline.cpp b/src/builtin_commandline.cpp index c7b82d7af..c25e60411 100644 --- a/src/builtin_commandline.cpp +++ b/src/builtin_commandline.cpp @@ -1,4 +1,6 @@ // Functions used for implementing the commandline builtin. +#include "config.h" // IWYU pragma: keep + #include #include #include diff --git a/src/builtin_complete.cpp b/src/builtin_complete.cpp index 90fc47146..f4e3b36f3 100644 --- a/src/builtin_complete.cpp +++ b/src/builtin_complete.cpp @@ -1,4 +1,6 @@ // Functions used for implementing the complete builtin. +#include "config.h" // IWYU pragma: keep + #include #include #include diff --git a/src/builtin_printf.cpp b/src/builtin_printf.cpp index a755d27ab..36b8e6382 100644 --- a/src/builtin_printf.cpp +++ b/src/builtin_printf.cpp @@ -48,6 +48,7 @@ // David MacKenzie // This file has been imported from source code of printf command in GNU Coreutils version 6.9. +#include "config.h" // IWYU pragma: keep #include #include diff --git a/src/builtin_set.cpp b/src/builtin_set.cpp index f4f49baf9..2333e0c0e 100644 --- a/src/builtin_set.cpp +++ b/src/builtin_set.cpp @@ -1,4 +1,6 @@ // Functions used for implementing the set builtin. +#include "config.h" // IWYU pragma: keep + #include #include #include diff --git a/src/builtin_test.cpp b/src/builtin_test.cpp index c179952e0..da63270d7 100644 --- a/src/builtin_test.cpp +++ b/src/builtin_test.cpp @@ -1,6 +1,8 @@ // Functions used for implementing the test builtin. // // Implemented from scratch (yes, really) by way of IEEE 1003.1 as reference. +#include "config.h" // IWYU pragma: keep + #include #include #include diff --git a/src/builtin_ulimit.cpp b/src/builtin_ulimit.cpp index f873c3854..6399cbdfe 100644 --- a/src/builtin_ulimit.cpp +++ b/src/builtin_ulimit.cpp @@ -1,4 +1,6 @@ // Functions used for implementing the ulimit builtin. +#include "config.h" // IWYU pragma: keep + #include #include #include diff --git a/src/color.cpp b/src/color.cpp index 7648449d9..41f41acfe 100644 --- a/src/color.cpp +++ b/src/color.cpp @@ -1,4 +1,6 @@ // Color class implementation. +#include "config.h" // IWYU pragma: keep + #include #include #include diff --git a/src/complete.cpp b/src/complete.cpp index ef713bb8f..8c38b2663 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -3,6 +3,8 @@ These functions are used for storing and retrieving tab-completion data, as well as for performing tab-completion. */ +#include "config.h" // IWYU pragma: keep + #include #include #include diff --git a/src/env.cpp b/src/env.cpp index 12eef036b..433b45f90 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -1,4 +1,6 @@ // Functions for setting and getting environment variables. +#include "config.h" // IWYU pragma: keep + #include #include #include diff --git a/src/event.cpp b/src/event.cpp index 3f5d05b9e..3da57dfa6 100644 --- a/src/event.cpp +++ b/src/event.cpp @@ -1,4 +1,6 @@ // Functions for handling event triggers. +#include "config.h" // IWYU pragma: keep + #include #include #include diff --git a/src/fish_indent.cpp b/src/fish_indent.cpp index 534437991..7e68e3101 100644 --- a/src/fish_indent.cpp +++ b/src/fish_indent.cpp @@ -15,6 +15,8 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ +#include "config.h" // IWYU pragma: keep + #include #include #include diff --git a/src/fish_key_reader.cpp b/src/fish_key_reader.cpp index ef587d3c3..7908bfef7 100644 --- a/src/fish_key_reader.cpp +++ b/src/fish_key_reader.cpp @@ -6,6 +6,8 @@ // carriage-return (\cM) and newline (\cJ). // // Type "exit" or "quit" to terminate the program. +#include "config.h" // IWYU pragma: keep + #include #include #include diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 8c1866b14..7f1c0a7d6 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -1,4 +1,5 @@ // Various bug and feature tests. Compiled and run by make test. +#include "config.h" // IWYU pragma: keep // IWYU pragma: no_include // IWYU pragma: no_include diff --git a/src/function.cpp b/src/function.cpp index 54559beee..fc3974722 100644 --- a/src/function.cpp +++ b/src/function.cpp @@ -2,6 +2,8 @@ // autoloading functions in the $fish_function_path. Actual function evaluation is taken care of by // the parser and to some degree the builtin handling library. // +#include "config.h" // IWYU pragma: keep + // IWYU pragma: no_include #include #include diff --git a/src/highlight.cpp b/src/highlight.cpp index f34505cf1..9dd6b224e 100644 --- a/src/highlight.cpp +++ b/src/highlight.cpp @@ -1,4 +1,6 @@ // Functions for syntax highlighting. +#include "config.h" // IWYU pragma: keep + // IWYU pragma: no_include #include #include diff --git a/src/history.cpp b/src/history.cpp index c4fb1f30b..ca61ed3ce 100644 --- a/src/history.cpp +++ b/src/history.cpp @@ -1,4 +1,6 @@ // History functions, part of the user interface. +#include "config.h" // IWYU pragma: keep + #include #include #include diff --git a/src/intern.cpp b/src/intern.cpp index 6171bc386..d92b142a8 100644 --- a/src/intern.cpp +++ b/src/intern.cpp @@ -1,4 +1,6 @@ // Library for pooling common strings. +#include "config.h" // IWYU pragma: keep + #include #include #include diff --git a/src/io.cpp b/src/io.cpp index 72bae3dba..b274897fd 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -1,4 +1,6 @@ // Utilities for io redirection. +#include "config.h" // IWYU pragma: keep + #include #include #include diff --git a/src/iothread.cpp b/src/iothread.cpp index 96a926a25..2780cda51 100644 --- a/src/iothread.cpp +++ b/src/iothread.cpp @@ -1,3 +1,5 @@ +#include "config.h" // IWYU pragma: keep + #include #include #include diff --git a/src/kill.cpp b/src/kill.cpp index c3b1000e0..17a10d85b 100644 --- a/src/kill.cpp +++ b/src/kill.cpp @@ -2,6 +2,8 @@ // // Works like the killring in emacs and readline. The killring is cut and paste with a memory of // previous cuts. It supports integration with the X clipboard. +#include "config.h" // IWYU pragma: keep + #include #include #include diff --git a/src/pager.cpp b/src/pager.cpp index dff8b1a58..222b510fb 100644 --- a/src/pager.cpp +++ b/src/pager.cpp @@ -1,3 +1,5 @@ +#include "config.h" // IWYU pragma: keep + // IWYU pragma: no_include #include #include diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp index 7989597f9..ee2da50d6 100644 --- a/src/parse_execution.cpp +++ b/src/parse_execution.cpp @@ -6,6 +6,8 @@ // // Non-fatal errors are printed as soon as they are encountered; otherwise you would have to wait // for the execution to finish to see them. +#include "config.h" // IWYU pragma: keep + #include #include #include diff --git a/src/parse_productions.cpp b/src/parse_productions.cpp index bb5b0e5fb..a9da3b325 100644 --- a/src/parse_productions.cpp +++ b/src/parse_productions.cpp @@ -1,3 +1,5 @@ +#include "config.h" // IWYU pragma: keep + #include #include "common.h" diff --git a/src/parse_tree.cpp b/src/parse_tree.cpp index 8bd7d4f9a..befa104b9 100644 --- a/src/parse_tree.cpp +++ b/src/parse_tree.cpp @@ -1,4 +1,6 @@ // Programmatic representation of fish code. +#include "config.h" // IWYU pragma: keep + #include #include #include diff --git a/src/parse_util.cpp b/src/parse_util.cpp index d1d921893..b78e4a8e4 100644 --- a/src/parse_util.cpp +++ b/src/parse_util.cpp @@ -2,6 +2,8 @@ // // This library can be seen as a 'toolbox' for functions that are used in many places in fish and // that are somehow related to parsing the code. +#include "config.h" // IWYU pragma: keep + #include #include #include diff --git a/src/parser.cpp b/src/parser.cpp index 17d845abb..90211df1e 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1,4 +1,6 @@ // The fish parser. Contains functions for parsing and evaluating code. +#include "config.h" // IWYU pragma: keep + #include #include #include diff --git a/src/parser_keywords.cpp b/src/parser_keywords.cpp index 340a886f2..4a99dd94b 100644 --- a/src/parser_keywords.cpp +++ b/src/parser_keywords.cpp @@ -1,4 +1,6 @@ // Functions having to do with parser keywords, like testing if a function is a block command. +#include "config.h" // IWYU pragma: keep + #include "parser_keywords.h" #include "common.h" #include "fallback.h" // IWYU pragma: keep diff --git a/src/path.cpp b/src/path.cpp index b5ee07902..c84789ce5 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -1,6 +1,8 @@ // Directory utilities. This library contains functions for locating configuration directories, for // testing if a command with a given name can be found in the PATH, and various other path-related // issues. +#include "config.h" // IWYU pragma: keep + #include #include #include diff --git a/src/postfork.cpp b/src/postfork.cpp index cb0760b44..08deb77cd 100644 --- a/src/postfork.cpp +++ b/src/postfork.cpp @@ -1,4 +1,6 @@ // Functions that we may safely call after fork(). +#include "config.h" // IWYU pragma: keep + #include #include #include diff --git a/src/print_help.cpp b/src/print_help.cpp index ad479023e..03ae38593 100644 --- a/src/print_help.cpp +++ b/src/print_help.cpp @@ -1,4 +1,6 @@ // Print help message for the specified command. +#include "config.h" // IWYU pragma: keep + #include #include #include diff --git a/src/sanity.cpp b/src/sanity.cpp index 895112ca4..df355935b 100644 --- a/src/sanity.cpp +++ b/src/sanity.cpp @@ -1,4 +1,6 @@ // Functions for performing sanity checks on the program state. +#include "config.h" // IWYU pragma: keep + #include #include "common.h" diff --git a/src/signal.cpp b/src/signal.cpp index f232b01c5..8aa558288 100644 --- a/src/signal.cpp +++ b/src/signal.cpp @@ -1,4 +1,6 @@ // The library for various signal related issues. +#include "config.h" // IWYU pragma: keep + #include #include #include diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 3bc5a1525..7357fd359 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -1,5 +1,7 @@ // A specialized tokenizer for tokenizing the fish language. In the future, the tokenizer should be // extended to support marks, tokenizing multiple strings and disposing of unused string segments. +#include "config.h" // IWYU pragma: keep + #include #include #include diff --git a/src/utf8.cpp b/src/utf8.cpp index afd0c7642..e5a4178a4 100644 --- a/src/utf8.cpp +++ b/src/utf8.cpp @@ -13,6 +13,8 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" // IWYU pragma: keep + #include // IWYU pragma: keep #include #include diff --git a/src/util.cpp b/src/util.cpp index 80c9771a4..bf44feabe 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -1,6 +1,8 @@ // Generic utilities library. // // Contains data structures such as automatically growing array lists, priority queues, etc. +#include "config.h" // IWYU pragma: keep + #include #include #include diff --git a/src/wcstringutil.cpp b/src/wcstringutil.cpp index 9fcb168ee..7e208469a 100644 --- a/src/wcstringutil.cpp +++ b/src/wcstringutil.cpp @@ -1,4 +1,6 @@ // Helper functions for working with wcstring. +#include "config.h" // IWYU pragma: keep + #include "wcstringutil.h" #include "common.h" diff --git a/src/wildcard.cpp b/src/wildcard.cpp index ace52a798..b48fe7635 100644 --- a/src/wildcard.cpp +++ b/src/wildcard.cpp @@ -1,5 +1,7 @@ // Fish needs it's own globbing implementation to support tab-expansion of globbed parameters. Also // provides recursive wildcards using **. +#include "config.h" // IWYU pragma: keep + #include #include #include From 14187f9e3f3ba14a287803c794b20bd9d913cdab Mon Sep 17 00:00:00 2001 From: David Adam Date: Sat, 7 May 2016 06:29:44 +0800 Subject: [PATCH 263/363] configure: drop manual checks for __EXTENSIONS__ 375bef44432a0fad7ebbd3634a3e950e2d457ec1 includes the appropriate autoconf method of checking for this feature flag. Work on #2999. --- configure.ac | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/configure.ac b/configure.ac index c97b8c51c..7dc9a73ef 100644 --- a/configure.ac +++ b/configure.ac @@ -292,21 +292,6 @@ if test $target_cpu = powerpc; then fi -# -# Solaris-specific flags go here -# - -AC_MSG_CHECKING([if we are under Solaris]) -case $target_os in - solaris*) - AC_DEFINE( __EXTENSIONS__, 1, [Macro to enable additional prototypes under Solaris]) - AC_MSG_RESULT(yes) - ;; - *) - AC_MSG_RESULT(no) - ;; -esac - # # BSD-specific flags go here # @@ -413,10 +398,6 @@ AC_DEFINE_UNQUOTED([WCHAR_T_BITS], [$WCHAR_T_BITS], [The size of wchar_t in bits # # * This test needs to be run _before_ testing for the presense of any # prototypes or other language functinality. -# -# * This test should be (but does not need to be) run after the -# conditional definition of __EXTENSIONS__, to avoid redundant tests. -# XCXXFLAGS="$CXXFLAGS" From f5dcb6a0cbe504e63f30d45627eb0ab5f617747f Mon Sep 17 00:00:00 2001 From: David Adam Date: Fri, 6 May 2016 23:54:38 +0100 Subject: [PATCH 264/363] configure: Use standard macro to enable Large File Support Work on #2999. --- configure.ac | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 7dc9a73ef..3b3c2ef83 100644 --- a/configure.ac +++ b/configure.ac @@ -197,8 +197,7 @@ AS_IF([test "$use_doxygen" != "no"], # where off_t can be either 32 or 64 bit, the latter size is used. On # other systems, this should do nothing. (Hopefully) # - -CXXFLAGS="$CXXFLAGS -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64" +AC_SYS_LARGEFILE # fish does not use exceptions From e39628bbe934a895ec6accac2446919ff8f13563 Mon Sep 17 00:00:00 2001 From: David Adam Date: Fri, 6 May 2016 23:55:07 +0100 Subject: [PATCH 265/363] configure: drop unnecessary feature flag checks Work on #2999. --- configure.ac | 43 ------------------------------------------- 1 file changed, 43 deletions(-) diff --git a/configure.ac b/configure.ac index 3b3c2ef83..05ed5b960 100644 --- a/configure.ac +++ b/configure.ac @@ -230,49 +230,6 @@ AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])], LDFLAGS="$prev_LDFLAGS" -# -# If we are compiling against glibc, set some flags to work around -# some rather stupid attempts to hide prototypes for *wprintf -# functions, as well as prototypes of various gnu extensions. -# - -AC_MSG_CHECKING([if we are compiling against glibc]) -AC_RUN_IFELSE( - [ - AC_LANG_PROGRAM( - [ - #include - #ifdef __GLIBC__ - #define STATUS 0 - #else - #define STATUS 1 - #endif - ], - [ - return STATUS; - ] - ) - ], - [glibc=yes], - [glibc=no] -) - -if test "$glibc" = yes; then - AC_MSG_RESULT(yes) - - # - # This gives us access to prototypes for gnu extensions and C99 - # functions if we are compiling agains glibc. All GNU extensions - # that are used must have a fallback implementation available in - # fallback.h, in order to keep fish working on non-gnu platforms. - # - - CXXFLAGS="$CXXFLAGS -D_GNU_SOURCE=1 -D_ISO99_SOURCE=1" -else - AC_MSG_RESULT(no) -fi - - # # Test cpu for special handling of ppc # From 6a5d89669e1bd2a5c98e61504d73d58ace11c880 Mon Sep 17 00:00:00 2001 From: David Adam Date: Mon, 9 May 2016 08:44:41 +0100 Subject: [PATCH 266/363] configure: drop tests for ancient platforms Work on #2999. --- configure.ac | 158 +---------------------------------------------- src/fallback.cpp | 8 --- src/fallback.h | 8 --- 3 files changed, 2 insertions(+), 172 deletions(-) diff --git a/configure.ac b/configure.ac index 05ed5b960..5907be3eb 100644 --- a/configure.ac +++ b/configure.ac @@ -94,6 +94,8 @@ AC_PROG_SED AC_LANG(C++) AC_USE_SYSTEM_EXTENSIONS +AC_CANONICAL_TARGET + echo "CXXFLAGS: $CXXFLAGS" # @@ -229,42 +231,6 @@ AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])], ]) LDFLAGS="$prev_LDFLAGS" - -# -# Test cpu for special handling of ppc -# -# This is used to skip use of tputs on ppc systems, since it seemed to -# be broken, at least on older debin-based systems. This is obviously -# not the right way to to detect whether this workaround should be -# used, since it catches far to many systems, but I do not have the -# hardware available to narrow this problem down, and in practice, it -# seems that tputs is never really needed. -# - -AC_CANONICAL_TARGET - -if test $target_cpu = powerpc; then - AC_DEFINE([TPUTS_KLUDGE],[1],[Evil kludge to get Power based machines to work]) -fi - - -# -# BSD-specific flags go here -# - -AC_MSG_CHECKING([if we are under BSD]) -case $target_os in - *bsd*) - AC_DEFINE( __BSD_VISIBLE, 1, [Macro to enable additional prototypes under BSD]) - AC_DEFINE( _NETBSD_SOURCE, 1, [Macro to enable additional prototypes under BSD]) - AC_MSG_RESULT(yes) - ;; - *) - AC_MSG_RESULT(no) - ;; -esac - - # # See if Linux procfs is present. This is used to get extra # information about running processes. @@ -329,121 +295,6 @@ AC_CHECK_SIZEOF(wchar_t) WCHAR_T_BITS=`expr 8 \* $ac_cv_sizeof_wchar_t` AC_DEFINE_UNQUOTED([WCHAR_T_BITS], [$WCHAR_T_BITS], [The size of wchar_t in bits.]) - -# -# On some platforms (Solaris 10) adding -std=c99 in turn requires that -# _POSIX_C_SOURCE be defined to 200112L otherwise several -# POSIX-specific, non-ISO-C99 types/prototypes are made unavailable -# e.g. siginfo_t. Defining _XOPEN_SOURCE to 600 is compatible with -# the _POSIX_C_SOURCE value and provides a little assurance that -# extension functions' prototypes are available, e.g. killpg(). -# -# Some other platforms (OS X), will remove types/prototypes/macros -# e.g. SIGWINCH if either _POSIX_C_SOURCE or _XOPEN_SOURCE is defined. -# -# This test adds these macros only if they enable a program that uses -# both Posix and non-standard features to compile, and that program -# does not compile without these macros. -# -# We try to make everyone happy. -# -# The ordering of the various autoconf tests is very critical as well: -# -# * This test needs to be run _after_ header detection tests, so that -# the proper headers are included. -# -# * This test needs to be run _before_ testing for the presense of any -# prototypes or other language functinality. - -XCXXFLAGS="$CXXFLAGS" - -echo checking how to use -D_XOPEN_SOURCE=600 and -D_POSIX_C_SOURCE=200112L... -local_found_posix_switch=no - -for i in "" "-D_POSIX_C_SOURCE=200112L" "-D_XOPEN_SOURCE=600 -D_POSIX_C_SOURCE=200112L"; do - - AC_MSG_CHECKING( if switches \"$i\" works) - CXXFLAGS="$XCXXFLAGS $i" - - # - # Try to run this program, which should test various extensions - # and Posix functionality. If this program works, then everything - # should work. Hopefully. - # - - AC_TRY_LINK( - [ - #include - #include - #include - - /* POSIX, C89 and C99: POSIX extends this header. - * For: kill(), killpg(), siginfo_t, sigset_t, - * struct sigaction, sigemptyset(), sigaction(), - * SIGIO and SIGWINCH. */ - #include - - #ifdef HAVE_SIGINFO_H - /* Neither POSIX, C89 nor C99: Solaris-specific (others?). - * For: siginfo_t (also defined by signal.h when in - * POSIX/extensions mode). */ - #include - #endif - - #ifdef HAVE_SYS_IOCTL_H - /* As above (under at least Linux and FreeBSD). */ - #include - #endif - - #ifdef HAVE_TERMIOS_H - #include - #endif - ], - [ - /* Avert high-level optimisation, by making the program's - * return value depend on all tested identifiers. */ - long ret = 0; - /* POSIX only: might be unhidden by _POSIX_C_SOURCE. */ - struct sigaction sa; - sigset_t ss; - siginfo_t info; - ret += (long)(void *)&info + kill( 0, 0 ) + - sigaction( 0, &sa, 0 ) + sigemptyset( &ss ); - /* Extended-POSIX: might be unhidden by _XOPEN_SOURCE. */ - ret += killpg( 0, 0 ); - /* Non-standard: might be hidden by the macros. */ - { - struct winsize termsize; - ret += (long)(void *)&termsize; - ret += SIGWINCH + TIOCGWINSZ + SIGIO; - } - return ret; - - ], - local_cv_use__posix_c_source=yes, - local_cv_use__posix_c_source=no, - ) - - if test x$local_cv_use__posix_c_source = xyes; then - AC_MSG_RESULT( yes ) - local_found_posix_switch=yes - break; - else - AC_MSG_RESULT( no ) - fi - -done - -# -# We didn't find any combination of switches that worked - revert to -# no switches and hope that the fallbacks work. A warning will be -# printed at the end of the configure script. -# - -if test ! x$local_found_posix_switch = xyes; then - CXXFLAGS="$XCXXFLAGS" -fi - # # Detect nanoseconds fields in struct stat # @@ -826,11 +677,6 @@ AC_ARG_WITH([extra-confdir], AC_CONFIG_FILES([Makefile]) AC_OUTPUT -if test ! x$local_found_posix_switch = xyes; then - echo "Can't find a combination of switches to enable common extensions like detecting window size." - echo "Some fish features may be disabled." -fi - echo "fish is now configured." echo "Use 'make' and 'make install' to build and install fish." diff --git a/src/fallback.cpp b/src/fallback.cpp index a5cff93d2..f5638e81e 100644 --- a/src/fallback.cpp +++ b/src/fallback.cpp @@ -42,14 +42,6 @@ #include "fallback.h" // IWYU pragma: keep #include "util.h" // IWYU pragma: keep -#ifdef TPUTS_KLUDGE -int tputs(const char *str, int affcnt, int (*fish_putc)(tputs_arg_t)) { - while (*str) { - fish_putc(*str++); - } -} -#endif - #ifdef TPARM_SOLARIS_KLUDGE #undef tparm diff --git a/src/fallback.h b/src/fallback.h index 59eba2242..97c72b77d 100644 --- a/src/fallback.h +++ b/src/fallback.h @@ -57,14 +57,6 @@ struct winsize { #endif -#ifdef TPUTS_KLUDGE - -/// Linux on PPC seems to have a tputs implementation that sometimes behaves strangely. This -/// fallback seems to fix things. -int tputs(const char *str, int affcnt, int (*fish_putc)(tputs_arg_t)); - -#endif - #ifdef TPARM_SOLARIS_KLUDGE /// Solaris tparm has a set fixed of paramters in it's curses implementation, work around this here. #define tparm tparm_solaris_kludge From 504b32f61be6bc8a1e08f9c8a75ab0ac1e89681b Mon Sep 17 00:00:00 2001 From: David Adam Date: Sun, 15 May 2016 19:36:19 +0000 Subject: [PATCH 267/363] configure: drop fwprintf test fwprintf would segfault on DragonFly BSD 1.4.0, released in January 2006. This was fixed by DragonFly BSD 1.4.4, released in April 2006. It seems unlikely that anyone is still running a ten-year-old, unsupported version, and hoping that fish will continue to build. I've checked this in virtual machines. Work on #2999. --- configure.ac | 35 ----------------------------------- src/fallback.cpp | 4 ---- 2 files changed, 39 deletions(-) diff --git a/configure.ac b/configure.ac index 5907be3eb..e50258b0b 100644 --- a/configure.ac +++ b/configure.ac @@ -405,41 +405,6 @@ AC_LINK_IFELSE( ) -# -# If we have a fwprintf in libc, test that it actually works. As of -# March 2006, it is broken under DragonFly BSD. -# - -if test "$ac_cv_func_fwprintf" = yes; then - - AC_MSG_CHECKING([if fwprintf is broken]) - AC_RUN_IFELSE( - [ - AC_LANG_PROGRAM( - [ - #include - #include - #include - #include - ], - [ - setlocale( LC_ALL, "" ); - fwprintf( stderr, L"%ls%ls", L"", L"fish:" ); - ] - ) - ], - [ - AC_MSG_RESULT(no) - ], - [ - AC_MSG_RESULT([yes]) - AC_DEFINE([HAVE_BROKEN_FWPRINTF], [1], [Define to 1 one if the implemented fwprintf is broken]) - ] - ) - -fi - - # Check for _nl_msg_cat_cntr symbol AC_MSG_CHECKING([for _nl_msg_cat_cntr symbol]) AC_TRY_LINK( diff --git a/src/fallback.cpp b/src/fallback.cpp index f5638e81e..92c3e1fcd 100644 --- a/src/fallback.cpp +++ b/src/fallback.cpp @@ -94,10 +94,6 @@ char *tparm_solaris_kludge(char *str, ...) { #define INTERNAL_FWPRINTF 1 #endif -#ifdef HAVE_BROKEN_FWPRINTF -#define INTERNAL_FWPRINTF 1 -#endif - #ifdef INTERNAL_FWPRINTF /// Internal function for the wprintf fallbacks. USed to write the specified number of spaces. From d0aa46158796196b79125573703024ae48c6c0dd Mon Sep 17 00:00:00 2001 From: David Adam Date: Sun, 15 May 2016 21:44:11 +0000 Subject: [PATCH 268/363] fallback: remove sysconf fallback sysconf was introduced in IEEE Std 1003.1-1988 (POSIX.1) and exists on every system I can find. Work on #2999. --- configure.ac | 2 +- src/fallback.cpp | 13 ------------- src/fallback.h | 5 ----- 3 files changed, 1 insertion(+), 19 deletions(-) diff --git a/configure.ac b/configure.ac index e50258b0b..48fdaf350 100644 --- a/configure.ac +++ b/configure.ac @@ -313,7 +313,7 @@ AC_STRUCT_DIRENT_D_TYPE AC_CHECK_FUNCS( wcsdup wcsndup wcslen wcscasecmp wcsncasecmp fwprintf ) AC_CHECK_FUNCS( futimes wcwidth wcswidth wcstok fputwc fgetwc ) AC_CHECK_FUNCS( wcstol wcslcat wcslcpy lrand48_r killpg ) -AC_CHECK_FUNCS( backtrace backtrace_symbols_fd sysconf getifaddrs ) +AC_CHECK_FUNCS( backtrace backtrace_symbols_fd getifaddrs ) AC_CHECK_FUNCS( futimens clock_gettime ) AC_CHECK_DECL( [mkostemp], [ AC_CHECK_FUNCS([mkostemp]) ] ) diff --git a/src/fallback.cpp b/src/fallback.cpp index 92c3e1fcd..15c5614f5 100644 --- a/src/fallback.cpp +++ b/src/fallback.cpp @@ -955,19 +955,6 @@ int backtrace(void **buffer, int size) { return 0; } char **backtrace_symbols_fd(void *const *buffer, int size, int fd) { return 0; } #endif -#ifndef HAVE_SYSCONF - -long sysconf(int name) { - if (name == _SC_ARG_MAX) { -#ifdef ARG_MAX - return ARG_MAX; -#endif - } - - return -1; -} -#endif - #ifndef HAVE_NAN double nan(char *tagp) { return 0.0 / 0.0; } #endif diff --git a/src/fallback.h b/src/fallback.h index 97c72b77d..241edb5ac 100644 --- a/src/fallback.h +++ b/src/fallback.h @@ -253,11 +253,6 @@ extern int _nl_msg_cat_cntr; int killpg(int pgr, int sig); #endif -#ifndef HAVE_SYSCONF -#define _SC_ARG_MAX 1 -long sysconf(int name); -#endif - #ifndef HAVE_NAN double nan(char *tagp); #endif From 44757c81afc41d71917f71bd644b2f0092bb4053 Mon Sep 17 00:00:00 2001 From: David Adam Date: Sun, 15 May 2016 22:08:43 +0000 Subject: [PATCH 269/363] fallback: remove fwprintf and friends fallbacks All modern operating systems implement fwprintf, including NetBSD (which introduced them in 2005). Work on #2999. --- configure.ac | 2 +- src/fallback.cpp | 411 ----------------------------------------------- src/fallback.h | 32 ---- 3 files changed, 1 insertion(+), 444 deletions(-) diff --git a/configure.ac b/configure.ac index 48fdaf350..c1d02404b 100644 --- a/configure.ac +++ b/configure.ac @@ -310,7 +310,7 @@ AC_STRUCT_DIRENT_D_TYPE # Check for presense of various functions used by fish # -AC_CHECK_FUNCS( wcsdup wcsndup wcslen wcscasecmp wcsncasecmp fwprintf ) +AC_CHECK_FUNCS( wcsdup wcsndup wcslen wcscasecmp wcsncasecmp ) AC_CHECK_FUNCS( futimes wcwidth wcswidth wcstok fputwc fgetwc ) AC_CHECK_FUNCS( wcstol wcslcat wcslcpy lrand48_r killpg ) AC_CHECK_FUNCS( backtrace backtrace_symbols_fd getifaddrs ) diff --git a/src/fallback.cpp b/src/fallback.cpp index 15c5614f5..518e907a9 100644 --- a/src/fallback.cpp +++ b/src/fallback.cpp @@ -90,417 +90,6 @@ char *tparm_solaris_kludge(char *str, ...) { #endif -#ifndef HAVE_FWPRINTF -#define INTERNAL_FWPRINTF 1 -#endif - -#ifdef INTERNAL_FWPRINTF - -/// Internal function for the wprintf fallbacks. USed to write the specified number of spaces. -static void pad(void (*writer)(wchar_t), int count) { - int i; - if (count < 0) return; - - for (i = 0; i < count; i++) { - writer(L' '); - } -} - -/// Generic formatting function. All other string formatting functions are secretly a wrapper around -/// this function. vgprintf does not implement all the filters supported by printf, only those that -/// are currently used by fish. vgprintf internally uses snprintf to implement the number outputs, -/// such as %f and %x. -/// -/// Currently supported functionality: -/// -/// - precision specification, both through .* and .N -/// - Padding through * and N -/// - Right padding using the - prefix -/// - long versions of all filters thorugh l and ll prefix -/// - Character output using %c -/// - String output through %s -/// - Floating point number output through %f -/// - Integer output through %d, %i, %u, %o, %x and %X -/// -/// For a full description on the usage of *printf, see use 'man 3 printf'. -static int vgwprintf(void (*writer)(wchar_t), const wchar_t *filter, va_list va) { - const wchar_t *filter_org = filter; - int count = 0; - - for (; *filter; filter++) { - if (*filter == L'%') { - int is_long = 0; - int width = -1; - filter++; - int loop = 1; - int precision = -1; - int pad_left = 1; - - if (iswdigit(*filter)) { - width = 0; - while ((*filter >= L'0') && (*filter <= L'9')) { - width = 10 * width + (*filter++ - L'0'); - } - } - - while (loop) { - switch (*filter) { - case L'l': { - // Long variable. - is_long++; - filter++; - 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: { - 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; - wcstring wide_ss; - if (is_long) { - ss = va_arg(va, wchar_t *); - } else { - char *ns = va_arg(va, char *); - - if (ns) { - wide_ss = str2wcstring(ns); - ss = wide_ss.c_str(); - } - } - - 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); - } - - break; - } - case L'd': - case L'i': - case L'o': - case L'u': - case L'x': - case L'X': { - char str[33]; - char *pos; - char format[16]; - int len; - - format[0] = 0; - strcat(format, "%"); - if (precision >= 0) strcat(format, ".*"); - switch (is_long) { - case 2: { - strcat(format, "ll"); - break; - } - case 1: { - strcat(format, "l"); - break; - } - } - - len = strlen(format); - format[len++] = (char)*filter; - format[len] = 0; - - switch (*filter) { - case L'd': - case L'i': { - switch (is_long) { - case 0: { - int d = va_arg(va, int); - if (precision >= 0) - snprintf(str, 32, format, precision, d); - else - snprintf(str, 32, format, d); - - break; - } - case 1: { - long d = va_arg(va, long); - if (precision >= 0) - snprintf(str, 32, format, precision, d); - else - snprintf(str, 32, format, d); - break; - } - case 2: { - long long d = va_arg(va, long long); - if (precision >= 0) - snprintf(str, 32, format, precision, d); - else - snprintf(str, 32, format, d); - break; - } - default: { - debug(0, L"Invalid length modifier in string %ls\n", - filter_org); - return -1; - } - } - break; - } - - case L'u': - case L'o': - case L'x': - case L'X': { - switch (is_long) { - case 0: { - unsigned d = va_arg(va, unsigned); - if (precision >= 0) - snprintf(str, 32, format, precision, d); - else - snprintf(str, 32, format, d); - break; - } - case 1: { - unsigned long d = va_arg(va, unsigned long); - if (precision >= 0) - snprintf(str, 32, format, precision, d); - else - snprintf(str, 32, format, d); - break; - } - case 2: { - unsigned long long d = va_arg(va, unsigned long long); - if (precision >= 0) - snprintf(str, 32, format, precision, d); - else - snprintf(str, 32, format, d); - break; - } - default: { - debug(0, L"Invalid length modifier in string %ls\n", - filter_org); - return -1; - } - } - break; - } - default: { - debug(0, L"Invalid filter %ls in string %ls\n", *filter, filter_org); - return -1; - } - } - - if ((width >= 0) && pad_left) { - int l = maxi(width - strlen(str), 0); - pad(writer, l); - count += l; - } - - pos = str; - - while (*pos) { - writer(*(pos++)); - count++; - } - - if ((width >= 0) && !pad_left) { - int l = maxi(width - strlen(str), 0); - pad(writer, l); - count += l; - } - - break; - } - - case L'f': { - char str[32]; - char *pos; - double val = va_arg(va, double); - - if (precision >= 0) { - if (width >= 0) { - snprintf(str, 32, "%*.*f", width, precision, val); - } else { - snprintf(str, 32, "%.*f", precision, val); - } - } else { - if (width >= 0) { - snprintf(str, 32, "%*f", width, val); - } else { - snprintf(str, 32, "%f", val); - } - } - - pos = str; - - while (*pos) { - writer(*(pos++)); - count++; - } - - break; - } - - case L'n': { - int *n = va_arg(va, int *); - - *n = count; - break; - } - case L'%': { - writer('%'); - count++; - break; - } - default: { - debug(0, L"Unknown switch %lc in string %ls\n", *filter, filter_org); - return -1; - } - } - } else { - writer(*filter); - count++; - } - } - - return count; -} - -/// Holds data for swprintf writer. -static struct { - int count; - int max; - wchar_t *pos; -} sw_data; - -/// Writers for string output. -static void sw_writer(wchar_t c) { - if (sw_data.count < sw_data.max) *(sw_data.pos++) = c; - sw_data.count++; -} - -int vswprintf(wchar_t *out, size_t n, const wchar_t *filter, va_list va) { - int written; - - sw_data.pos = out; - sw_data.max = n; - sw_data.count = 0; - written = vgwprintf(&sw_writer, filter, va); - if (written < n) { - *sw_data.pos = 0; - } else { - written = -1; - } - - return written; -} - -int swprintf(wchar_t *out, size_t n, const wchar_t *filter, ...) { - va_list va; - int written; - - va_start(va, filter); - written = vswprintf(out, n, filter, va); - va_end(va); - return written; -} - -/// Holds auxiliary data for fwprintf and wprintf writer. -static FILE *fw_data; - -static void fw_writer(wchar_t c) { fputwc(c, fw_data); } - -/// Writers for file output. -int vfwprintf(FILE *f, const wchar_t *filter, va_list va) { - fw_data = f; - return vgwprintf(&fw_writer, filter, va); -} - -int fwprintf(FILE *f, const wchar_t *filter, ...) { - va_list va; - int written; - - va_start(va, filter); - written = vfwprintf(f, filter, va); - va_end(va); - return written; -} - -int vwprintf(const wchar_t *filter, va_list va) { return vfwprintf(stdout, filter, va); } - -int wprintf(const wchar_t *filter, ...) { - va_list va; - int written; - - va_start(va, filter); - written = vwprintf(filter, va); - va_end(va); - return written; -} - -#endif - #ifndef HAVE_FGETWC wint_t fgetwc(FILE *stream) { wchar_t res; diff --git a/src/fallback.h b/src/fallback.h index 241edb5ac..13caf0b39 100644 --- a/src/fallback.h +++ b/src/fallback.h @@ -63,38 +63,6 @@ struct winsize { char *tparm_solaris_kludge(char *str, ...); #endif -#ifndef HAVE_FWPRINTF -/// Print formated string. Some operating systems (Like NetBSD) do not have wide string formating -/// functions. Therefore we implement our own. Not at all complete. Supports wide and narrow -/// characters, strings and decimal numbers, position (%n), field width and precision. -int fwprintf(FILE *f, const wchar_t *format, ...); - -/// Print formated string. Some operating systems (Like NetBSD) do not have wide string formating -/// functions. Therefore we define our own. Not at all complete. Supports wide and narrow -/// characters, strings and decimal numbers, position (%n), field width and precision. -int swprintf(wchar_t *str, size_t l, const wchar_t *format, ...); - -/// Print formated string. Some operating systems (Like NetBSD) do not have wide string formating -/// functions. Therefore we define our own. Not at all complete. Supports wide and narrow -/// characters, strings and decimal numbers, position (%n), field width and precision. -int wprintf(const wchar_t *format, ...); - -/// Print formated string. Some operating systems (Like NetBSD) do not have wide string formating -/// functions. Therefore we define our own. Not at all complete. Supports wide and narrow -/// characters, strings and decimal numbers, position (%n), field width and precision. -int vwprintf(const wchar_t *filter, va_list va); - -/// Print formated string. Some operating systems (Like NetBSD) do not have wide string formating -/// functions. Therefore we define our own. Not at all complete. Supports wide and narrow -/// characters, strings and decimal numbers, position (%n), field width and precision. -int vfwprintf(FILE *f, const wchar_t *filter, va_list va); - -/// Print formated string. Some operating systems (Like NetBSD) do not have wide string formating -/// functions. Therefore we define our own. Not at all complete. Supports wide and narrow -/// characters, 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); -#endif - #ifndef HAVE_FGETWC // Fallback implementation of fgetwc. wint_t fgetwc(FILE *stream); From db18449f4cdc190c74a2ba3228fbd1e0ca467154 Mon Sep 17 00:00:00 2001 From: David Adam Date: Sun, 15 May 2016 23:19:27 +0000 Subject: [PATCH 270/363] fallback: drop fallbacks for C99/C++0x wide character functions Drops configure check for wcsdup, wcslen, wcscasecmp, wcsncasecmp, wcwidth, wcswidth, wcstok, fputwc, fgetwc, and wcstol. Drop the fallback implementations of these on non-Snow Leopard platforms. Work on #2999. --- configure.ac | 8 +- src/fallback.cpp | 196 +---------------------------------------------- src/fallback.h | 78 ------------------- 3 files changed, 6 insertions(+), 276 deletions(-) diff --git a/configure.ac b/configure.ac index c1d02404b..5abf8fecf 100644 --- a/configure.ac +++ b/configure.ac @@ -307,12 +307,12 @@ AC_CHECK_MEMBERS([struct stat.st_mtim.tv_nsec]) AC_STRUCT_DIRENT_D_TYPE # -# Check for presense of various functions used by fish +# Check for presence of various functions used by fish # -AC_CHECK_FUNCS( wcsdup wcsndup wcslen wcscasecmp wcsncasecmp ) -AC_CHECK_FUNCS( futimes wcwidth wcswidth wcstok fputwc fgetwc ) -AC_CHECK_FUNCS( wcstol wcslcat wcslcpy lrand48_r killpg ) +AC_CHECK_FUNCS( wcsndup ) +AC_CHECK_FUNCS( futimes ) +AC_CHECK_FUNCS( wcslcat wcslcpy lrand48_r killpg ) AC_CHECK_FUNCS( backtrace backtrace_symbols_fd getifaddrs ) AC_CHECK_FUNCS( futimens clock_gettime ) diff --git a/src/fallback.cpp b/src/fallback.cpp index 518e907a9..74635acc8 100644 --- a/src/fallback.cpp +++ b/src/fallback.cpp @@ -90,135 +90,6 @@ char *tparm_solaris_kludge(char *str, ...) { #endif -#ifndef HAVE_FGETWC -wint_t fgetwc(FILE *stream) { - wchar_t res; - mbstate_t state = {}; - - while (1) { - int b = fgetc(stream); - if (b == EOF) { - return WEOF; - } - - if (MB_CUR_MAX == 1) // single-byte locale, all values are legal - { - return b; - } - - char bb = b; - size_t sz = mbrtowc(&res, &bb, 1, &state); - switch (sz) { - case -1: { - return WEOF; - } - case -2: { - break; - } - case 0: { - return 0; - } - default: { return res; } - } - } -} -#endif - -#ifndef HAVE_FPUTWC -wint_t fputwc(wchar_t wc, FILE *stream) { - int res = 0; - mbstate_t state = {}; - char s[MB_CUR_MAX + 1] = {}; - - if (MB_CUR_MAX == 1) // single-byte locale (C/POSIX/ISO-8859) - { - // If `wc` contains a wide character we emit a question-mark. - if (wc & ~0xFF) { - wc = '?'; - } - s[0] = (char)wc; - res = fputs(s, stream); - } else { - size_t len = wcrtomb(s, wc, &state); - if (len == (size_t)-1) { - debug(1, L"Wide character %d has no narrow representation", wc); - } else { - res = fputs(s, stream); - } - } - - return res == EOF ? WEOF : wc; -} -#endif - -#ifndef HAVE_WCSTOK - -/// Used by fallback wcstok. Borrowed from glibc. -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; - - for (p = wcs; *p != L'\0'; ++p) { - for (a = accept; *a != L'\0'; ++a) - if (*p == *a) break; - - if (*a == L'\0') - return count; - else - ++count; - } - return count; -} - -/// Used by fallback wcstok. Borrowed from glibc. -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; -} - -/// Fallback wcstok implementation. Borrowed from glibc. -wchar_t *wcstok(wchar_t *wcs, const wchar_t *delim, wchar_t **save_ptr) { - wchar_t *result; - - if (wcs == NULL) { - if (*save_ptr == 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 { - // Terminate the token and make *SAVE_PTR point past it. - *wcs = L'\0'; - *save_ptr = wcs + 1; - } - return result; -} - -#endif - /// Fallback implementations of wcsdup and wcscasecmp. On systems where these are not needed (e.g. /// building on Linux) these should end up just being stripped, as they are static functions that /// are not referenced in this file. @@ -277,39 +148,8 @@ int wcsncasecmp_use_weak(const wchar_t *s1, const wchar_t *s2, size_t n) { return wcsncasecmp_fallback(s1, s2, n); } -#else //__APPLE__ - -#ifndef HAVE_WCSDUP -wchar_t *wcsdup(const wchar_t *in) { return wcsdup_fallback(in); } -#endif - -#ifndef HAVE_WCSCASECMP -int wcscasecmp(const wchar_t *a, const wchar_t *b) { return wcscasecmp_fallback(a, b); } -#endif #endif //__APPLE__ -#ifndef HAVE_WCSLEN -size_t wcslen(const wchar_t *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, size_t count) { - return wcsncasecmp_fallback(a, b, count); -} -#endif - -#ifndef HAVE_WCWIDTH -int wcwidth(wchar_t c) { - if (c < 32) return 0; - if (c == 127) return 0; - return 1; -} -#endif - #ifndef HAVE_WCSNDUP wchar_t *wcsndup(const wchar_t *in, size_t c) { wchar_t *res = (wchar_t *)malloc(sizeof(wchar_t) * (c + 1)); @@ -337,39 +177,6 @@ long convert_digit(wchar_t d, int base) { return res; } -#ifndef HAVE_WCSTOL -long wcstol(const wchar_t *nptr, wchar_t **endptr, int base) { - long long res = 0; - int is_set = 0; - if (base > 36) { - errno = EINVAL; - return 0; - } - - while (1) { - long nxt = convert_digit(*nptr, base); - if (endptr != 0) *endptr = (wchar_t *)nptr; - if (nxt < 0) { - if (!is_set) { - errno = EINVAL; - } - return res; - } - res = (res * base) + nxt; - is_set = 1; - if (res > LONG_MAX) { - errno = ERANGE; - return LONG_MAX; - } - if (res < LONG_MIN) { - errno = ERANGE; - return LONG_MIN; - } - nptr++; - } -} - -#endif #ifndef HAVE_WCSLCAT /*$OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $*/ @@ -548,7 +355,8 @@ char **backtrace_symbols_fd(void *const *buffer, int size, int fd) { return 0; } double nan(char *tagp) { 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. +// Big hack to use our versions of wcswidth where we know them to be broken, which is +// EVERYWHERE (https://github.com/fish-shell/fish-shell/issues/2199) #ifndef HAVE_BROKEN_WCWIDTH #define HAVE_BROKEN_WCWIDTH 1 #endif diff --git a/src/fallback.h b/src/fallback.h index 13caf0b39..97f495236 100644 --- a/src/fallback.h +++ b/src/fallback.h @@ -38,14 +38,6 @@ typedef int tputs_arg_t; typedef char tputs_arg_t; #endif -#ifndef SIGIO -#define SIGIO SIGUSR1 -#endif - -#ifndef SIGWINCH -#define SIGWINCH SIGUSR2 -#endif - #ifndef HAVE_WINSIZE /// Structure used to get the size of a terminal window. struct winsize { @@ -63,32 +55,6 @@ struct winsize { char *tparm_solaris_kludge(char *str, ...); #endif -#ifndef HAVE_FGETWC -// Fallback implementation of fgetwc. -wint_t fgetwc(FILE *stream); -#endif - -#ifndef HAVE_FPUTWC -// Fallback implementation of fputwc. -wint_t fputwc(wchar_t wc, FILE *stream); -#endif - -#ifndef HAVE_WCSTOK -/// Fallback implementation of wcstok. Uses code borrowed from glibc. -wchar_t *wcstok(wchar_t *wcs, const wchar_t *delim, wchar_t **ptr); -#endif - -#ifndef HAVE_WCWIDTH -/// Return the number of columns used by a character. This is a libc function, but the prototype for -/// this function is missing in some libc implementations. -/// -/// Fish has a fallback implementation in case the implementation is missing altogether. In locales -/// without a native wcwidth, Unicode is probably so broken that it isn't worth trying to implement -/// a 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); -#endif - /// On OS X, use weak linking for wcsdup and wcscasecmp. Weak linking allows you to call the /// function only if it exists at runtime. You can detect it by testing the function pointer against /// NULL. To avoid making the callers do that, redefine wcsdup to wcsdup_use_weak, and likewise with @@ -103,45 +69,8 @@ int wcsncasecmp_use_weak(const wchar_t *s1, const wchar_t *s2, size_t n); #define wcsdup(a) wcsdup_use_weak((a)) #define wcscasecmp(a, b) wcscasecmp_use_weak((a), (b)) #define wcsncasecmp(a, b, c) wcsncasecmp_use_weak((a), (b), (c)) - -#else - -#ifndef HAVE_WCSDUP -/// Create a duplicate string. Wide string version of strdup. Will automatically exit if out of -/// memory. -wchar_t *wcsdup(const wchar_t *in); -#endif - -#ifndef HAVE_WCSCASECMP -/// Case insensitive string compare function. Wide string version of strcasecmp. -/// -/// This implementation of wcscasecmp does not take into account esoteric locales where uppercase -/// and lowercase do not cleanly transform between each other. Hopefully this should be fine since -/// fish only uses this function with one of the strings supplied by 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); -#endif #endif //__APPLE__ -#ifndef HAVE_WCSLEN -/// Fallback for wclsen. Returns the length of the specified string. -size_t wcslen(const wchar_t *in); -#endif - -#ifndef HAVE_WCSNCASECMP -/// Case insensitive string compare function. Wide string version of strncasecmp. -/// -/// This implementation of wcsncasecmp does not take into account esoteric locales where uppercase -/// and lowercase do not cleanly transform between each other. Hopefully this should be fine since -/// fish only uses this function with one of the strings supplied by 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, size_t count); - -/// Returns a newly allocated wide character string wich is a copy of the string in, but of length c -/// or shorter. The returned string is always null terminated, and the null is not included in the -/// string length. -#endif - #ifndef HAVE_WCSNDUP /// 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); @@ -152,13 +81,6 @@ wchar_t *wcsndup(const wchar_t *in, size_t c); /// is exported. long convert_digit(wchar_t d, int base); -#ifndef HAVE_WCSTOL -/// Fallback implementation. Convert a wide character string to a number in the specified base. This -/// functions is the wide character string equivalent of strtol. For bases of 10 or lower, 0..9 are -/// used to represent numbers. For bases below 36, a-z and A-Z are used to represent numbers higher -/// than 9. Higher bases than 36 are not supported. -long wcstol(const wchar_t *nptr, wchar_t **endptr, int base); -#endif #ifndef HAVE_WCSLCAT /// Appends src to string dst of size siz (unlike wcsncat, siz is the full size of dst, not space /// left). At most siz-1 characters will be copied. Always NUL terminates (unless siz <= From 7c2c516353bbf6fae20fb5ec8fba373cf70d3aba Mon Sep 17 00:00:00 2001 From: David Adam Date: Mon, 16 May 2016 21:30:31 +0000 Subject: [PATCH 271/363] move convert_digit from fallback to common It's not required as part of fallback functions any more. --- src/common.cpp | 16 ++++++++++++++++ src/common.h | 4 ++++ src/fallback.cpp | 16 ---------------- src/fallback.h | 5 ----- 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/common.cpp b/src/common.cpp index aa27c5371..7bd909158 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -1919,3 +1919,19 @@ wchar_t **make_null_terminated_array(const wcstring_list_t &lst) { char **make_null_terminated_array(const std::vector &lst) { return make_null_terminated_array_helper(lst); } + +long convert_digit(wchar_t d, int base) { + long res = -1; + if ((d <= L'9') && (d >= L'0')) { + res = d - L'0'; + } else if ((d <= L'z') && (d >= L'a')) { + res = d + 10 - L'a'; + } else if ((d <= L'Z') && (d >= L'A')) { + res = d + 10 - L'A'; + } + if (res >= base) { + res = -1; + } + + return res; +} diff --git a/src/common.h b/src/common.h index ea0805f02..e59dfc0e8 100644 --- a/src/common.h +++ b/src/common.h @@ -765,4 +765,8 @@ extern "C" { __attribute__((noinline)) void debug_thread_error(void); } +/// Converts from wide char to digit in the specified base. If d is not a valid digit in the +/// specified base, return -1. +long convert_digit(wchar_t d, int base); + #endif diff --git a/src/fallback.cpp b/src/fallback.cpp index 74635acc8..7a8291247 100644 --- a/src/fallback.cpp +++ b/src/fallback.cpp @@ -161,22 +161,6 @@ wchar_t *wcsndup(const wchar_t *in, size_t c) { } #endif -long convert_digit(wchar_t d, int base) { - long res = -1; - if ((d <= L'9') && (d >= L'0')) { - res = d - L'0'; - } else if ((d <= L'z') && (d >= L'a')) { - res = d + 10 - L'a'; - } else if ((d <= L'Z') && (d >= L'A')) { - res = d + 10 - L'A'; - } - if (res >= base) { - res = -1; - } - - return res; -} - #ifndef HAVE_WCSLCAT /*$OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $*/ diff --git a/src/fallback.h b/src/fallback.h index 97f495236..b28e7b179 100644 --- a/src/fallback.h +++ b/src/fallback.h @@ -76,11 +76,6 @@ int wcsncasecmp_use_weak(const wchar_t *s1, const wchar_t *s2, size_t n); wchar_t *wcsndup(const wchar_t *in, size_t c); #endif -/// Converts from wide char to digit in the specified base. If d is not 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); - #ifndef HAVE_WCSLCAT /// Appends src to string dst of size siz (unlike wcsncat, siz is the full size of dst, not space /// left). At most siz-1 characters will be copied. Always NUL terminates (unless siz <= From 9192bf1db5b8287a507d7a479a69a2560aae260a Mon Sep 17 00:00:00 2001 From: David Adam Date: Tue, 17 May 2016 21:15:48 +0000 Subject: [PATCH 272/363] configure: fix _nl_msg_cat_cntr check --- configure.ac | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.ac b/configure.ac index 5abf8fecf..3600ed96e 100644 --- a/configure.ac +++ b/configure.ac @@ -412,6 +412,7 @@ AC_TRY_LINK( #if HAVE_LIBINTL_H #include #endif + #include ], [ extern int _nl_msg_cat_cntr; From 21fc2decd741d3a18c88445e11de8860148060d0 Mon Sep 17 00:00:00 2001 From: David Adam Date: Wed, 18 May 2016 23:00:30 +0000 Subject: [PATCH 273/363] lint, style: use git plumbing commands Rather than using porcelain commands, try using plumbing for a more stable interface with less string munging. --- build_tools/lint.fish | 5 +++-- build_tools/style.fish | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/build_tools/lint.fish b/build_tools/lint.fish index f40d4c354..2b21402c6 100755 --- a/build_tools/lint.fish +++ b/build_tools/lint.fish @@ -48,10 +48,11 @@ if test $all = yes else # We haven't been asked to lint all the source. If there are uncommitted # changes lint those, else lint the files in the most recent commit. - set files (git status --porcelain --short --untracked-files=all | sed -e 's/^ *[^ ]* *//') + # Select (cached files) (modified but not cached, and untracked files) + set files (git diff-index --cached HEAD --name-only) (git ls-files --exclude-standard --others --modified) if not set -q files[1] # No pending changes so lint the files in the most recent commit. - set files (git show --word-diff=porcelain --name-only --pretty=oneline)[2..-1] + set files (git diff-tree --no-commit-id --name-only -r HEAD) end # Extract just the C/C++ files that exist. diff --git a/build_tools/style.fish b/build_tools/style.fish index b5b3b8719..27f89804b 100755 --- a/build_tools/style.fish +++ b/build_tools/style.fish @@ -34,12 +34,13 @@ if test $all = yes else # We haven't been asked to reformat all the source. If there are uncommitted changes reformat # those using `git clang-format`. Else reformat the files in the most recent commit. - set files (git status --porcelain --short --untracked-files=all | sed -e 's/^ *[^ ]* *//') + # Select (cached files) (modified but not cached, and untracked files) + set files (git diff-index --cached HEAD --name-only) (git ls-files --exclude-standard --others --modified) if set -q files[1] set git_clang_format yes else # No pending changes so lint the files in the most recent commit. - set files (git show --name-only --pretty=oneline | tail --lines=+2) + set files (git diff-tree --no-commit-id --name-only -r HEAD) end # Extract just the C/C++ files that exist. From 8dc74de92e12ed6c49e4c34273a7081481ceb85d Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 19 May 2016 13:23:00 +0200 Subject: [PATCH 274/363] Add completion for `ip` This is quite ugly because the syntax is ugly, the documentation both under- and overspecified at the same time (a BNF that isn't...) and it has a lot of functionality. But the completion works half-decent for `ip address`, so let's ship it. --- share/completions/ip.fish | 344 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 344 insertions(+) create mode 100644 share/completions/ip.fish diff --git a/share/completions/ip.fish b/share/completions/ip.fish new file mode 100644 index 000000000..b7ded602e --- /dev/null +++ b/share/completions/ip.fish @@ -0,0 +1,344 @@ +# ip(8) completion for fish + +# The difficulty here is that ip allows abbreviating options, so we need to complete "ip a" like "ip address", but not "ip m" like "ip mroute" +# Also the manpage and even the grammar it accepts is utter shite (options can only be before commands, some things are only in the BNF, others only in the text) +# It also quite likes the word "dev", even though it needs it less than the BNF specifies + +set -l ip_commands link address addrlabel route rule neigh ntable tunnel tuntap maddr mroute mrule monitor xfrm netns l2tp tcp_metrics +set -l ip_addr a ad add addr addre addres address +set -l ip_link l li lin link +set -l ip_all_commands $ip_commands $ip_addr $ip_link + +function __fish_ip_commandwords + set -l skip 0 + set -l cmd (commandline -opc) + # HACK: Handle and/or/not specially because they have hardcoded completion behavior + # that doesn't remove them from the commandline + if contains -- $cmd[1] and or not + set -e cmd[1] + end + # Remove the first word because it's "ip" or an alias for it + set -e cmd[1] + set -l have_command 0 + for word in $cmd + switch $word + # Normalize the commands to their full form - `ip a` is `ip address` + # This can't be just an unambiguity check because there's also `ip addrlabel` + case a ad add addr addre addres address + # "addr add" is a thing, so we can only echo "address" if it's actually the command + if test $have_command = 0 + set have_command 1 + echo address + else + echo $word + end + case l li lin link + if test $have_command = 0 + set have_command 1 + echo link + else + echo $word + end + case "addrl*" + if test $have_command = 0 + set have_command 1 + echo addrlabel + else + echo $word + end + case r ro rou rout route + if test $have_command = 0 + set have_command 1 + echo route + else + echo $word + end + case "ru*" + if test $have_command = 0 + set have_command 1 + echo rule + else + echo $word + end + case n ne nei neig neigh + if test $have_command = 0 + set have_command 1 + echo neigh + else + echo $word + end + case nt + if test $have_command = 0 + set have_command 1 + echo ntable + else + echo $word + end + case t tu tun tunn tunne tunnel + if test $have_command = 0 + set have_command 1 + echo tunnel + else + echo $word + end + case "tunt*" + if test $have_command = 0 + set have_command 1 + echo tuntap + else + echo $word + end + case m ma mad madd maddr maddre maddres maddress + if test $have_command = 0 + set have_command 1 + echo maddress + else + echo $word + end + case mr "mro*" + if test $have_command = 0 + set have_command 1 + echo mroute + else + echo $word + end + case "mru*" + if test $have_command = 0 + set have_command 1 + echo mrule + else + echo $word + end + case "mo*" + if test $have_command = 0 + set have_command 1 + echo monitor + else + echo $word + end + case "x*" + if test $have_command = 0 + set have_command 1 + echo xfrm + else + echo $word + end + case "net*" + if test $have_command = 0 + set have_command 1 + echo netns + else + echo $word + end + case "l*" + if test $have_command = 0 + set have_command 1 + echo l2tp + else + echo $word + end + case "tc*" + if test $have_command = 0 + set have_command 1 + echo tcp_metrics + else + echo $word + end + case "to*" + if test $have_command = 0 + set have_command 1 + echo token + else + echo $word + end + case '-n' '-netns' '--netns' + if test $have_command = 0 + set skip 1 + else + echo $word + end + case '-*' + test $have_command = 0; and continue + echo $word + case '*' + if test $skip = 1 + set skip 0 + continue + end + echo $word + end + end + # Print an empty line if the current token is empty, so we know that the one before it is finished + # TODO: For some reason it is necessary to always print the current token - why doesn't the above loop catch it? + set -l token (commandline -ct) + echo $token +end + +function __fish_ip_device + ip -o link show | while read a b c + printf '%s\t%s\n' (string replace ':' '' -- $b) "Device" + end +end + +function __fish_ip_scope + if test -r /etc/iproute2/rt_scopes + string replace -r '#.*' '' < /etc/iproute2/rt_scopes \ + | string match -v '^\s*$' \ + | string replace -r '(\S+)\s*(\S+)' '$1\t$2\n$2\t$1' \ + | string match -rv '^(global|link|host).*' # Ignore scopes with better descriptions + end + # Predefined scopes + printf '%s\t%s\n' global "Address is globally valid" \ + link "Address is link-local, only valid on this device" \ + host "Address is only valid on this host" +end + +function __fish_complete_ip + set -l cmd (__fish_ip_commandwords) + set -l count (count $cmd) + switch "$cmd[1]" + case address + # We're still _on_ the second word, which is the subcommand + if not set -q cmd[3] + printf '%s\t%s\n' add "Add new protocol address" \ + delete "Delete protocol address" \ + show "Look at protocol addresses" \ + flush "Flush protocol addresses" + else + switch $cmd[2] + # Change and replace are undocumented (apart from mentions in the BNF) + case add change replace + switch $count + case 3 + # __fish_ip_complete_ip + case '*' + switch $cmd[-2] + case dev + __fish_ip_device + case scope + __fish_ip_scope + # TODO: Figure out how to complete these + case label + # Prefix + case local peer broadcast + # Address + case valid_lft preferred_lft + # Lifetime + case '*' + printf '%s\t%s\n' forever "Keep address valid forever" \ + home "(Ipv6 only) Designate address as home adress" \ + nodad "(Ipv6 only) Don't perform duplicate address detection" \ + dev "Add address to specified device" \ + scope "Set scope of address" \ + label "Tag address with label" + end + end + case delete + switch $count + case 3 + ip -o addr show | while read a b c d e + echo $d + end + case 4 + # A dev argument is mandatory, but contrary to the BNF, other things (like "scope") are also valid here + # And yes, unlike e.g. show, this _needs_ the "dev" before the device + # Otherwise it barfs and says "??? prefix is expected" + # Anyway, try to steer the user towards supplying a device + echo dev + case 5 + switch $cmd[-2] + case dev + ip -o addr show | string match "*$cmd[3]*" | while read a b c + echo $b + end + # TODO: Moar + end + case show save flush # These take the same args + switch $cmd[-2] + case dev + __fish_ip_device + case scope + __fish_ip_scope + case to + # Prefix + case label + # Label-pattern + case '*' + printf '%s\t%s\n' up "Only active devices" \ + dev "Limit to a certain device" \ + scope "Limit scope" \ + to "Limit prefix" \ + label "Limit by label" \ + dynamic "(Ipv6 only) Limit to dynamic addresses" \ + permanent "(Ipv6 only) Limit to permanent addresses" + __fish_ip_device + # TODO: Moar + end + end + end + end + case link + if not set -q cmd[3] + printf '%s\t%s\n' add "Add virtual link" \ + delete "Delete virtual link" \ + set "Change device attributes" \ + show "Display device attributes" \ + help "Display help" + else + # TODO: Add moar + switch $cmd[2] + case add + switch $cmd[-2] + case link + __fish_ip_device + case name + case type + printf '%s\t%s\n' \ + macvtap "Virtual interface based on link layer address (MAC) and TAP." \ + vcan "Virtual Controller Area Network interface" \ + veth "Virtual ethernet interface" \ + vlan "802.1q tagged virtual LAN interface" \ + vxlan "Virtual eXtended LAN" \ + ip6tnl "Virtual tunnel interface IPv4|IPv6 over IPv6" \ + ipip "Virtual tunnel interface IPv4 over IPv4" \ + sit "Virtual tunnel interface IPv6 over IPv4" \ + gre "Virtual tunnel interface GRE over IPv4" \ + gretap "Virtual L2 tunnel interface GRE over IPv4" \ + ip6gre "Virtual tunnel interface GRE over IPv6" \ + ip6gretap "Virtual L2 tunnel interface GRE over IPv6" \ + vti "Virtual tunnel interface" \ + nlmon "Netlink monitoring device" \ + ipvlan "Interface for L3 (IPv6/IPv4) based VLANs" \ + lowpan "Interface for 6LoWPAN (IPv6) over IEEE 802.15.4 / Bluetooth" \ + geneve "GEneric NEtwork Virtualization Encapsulation" + end + case delete + case set + case show + case help + end + end + end +end + +complete -f -c ip +complete -f -c ip -a '(__fish_complete_ip)' +complete -f -c ip -n "not __fish_seen_subcommand_from $ip_all_commands" -a "$ip_commands" +# Yes, ip only takes options before "objects" +complete -c ip -s b -l batch -d "Read commands from file or stdin" -n "not __fish_seen_subcommand_from $ip_commands" +complete -c ip -l force -d "Don't terminate on errors in batch mode" -n "not __fish_seen_subcommand_from $ip_commands" +complete -c ip -s V -l Version -d "Print the version" -n "not __fish_seen_subcommand_from $ip_commands" +complete -c ip -f -s s -l stats -d "Output more information" -n "not __fish_seen_subcommand_from $ip_commands" +complete -c ip -f -s d -l details -d "Output more detailed information" -n "not __fish_seen_subcommand_from $ip_commands" +complete -c ip -f -s l -l loops -d "Specify maximum number of loops for 'ip addr flush'" -n "not __fish_seen_subcommand_from $ip_commands" +complete -c ip -f -s f -l family -d "The protocol family to use" -a "inet inet6 bridge ipx dnet link any" -n "not __fish_seen_subcommand_from $ip_commands" +complete -c ip -f -s 4 -d "Short for --family inet" -n "not __fish_seen_subcommand_from $ip_commands" +complete -c ip -f -s 6 -d "Short for --family inet6" -n "not __fish_seen_subcommand_from $ip_commands" +complete -c ip -f -s B -d "Short for --family bridge" -n "not __fish_seen_subcommand_from $ip_commands" +complete -c ip -f -s D -d "Short for --family decnet" -n "not __fish_seen_subcommand_from $ip_commands" +complete -c ip -f -s I -d "Short for --family ipx" -n "not __fish_seen_subcommand_from $ip_commands" +complete -c ip -f -s O -d "Short for --family link" -n "not __fish_seen_subcommand_from $ip_commands" +complete -c ip -f -s o -l oneline -d "Output on one line" -n "not __fish_seen_subcommand_from $ip_commands" +complete -c ip -f -s r -l resolve -d "Resolve names and print them instead of addresses" -n "not __fish_seen_subcommand_from $ip_commands" +complete -c ip -f -s n -l net -l netns -d "Use specified network namespace" -n "not __fish_seen_subcommand_from $ip_commands" +complete -c ip -f -s a -l all -d "Execute command for all objects" -n "not __fish_seen_subcommand_from $ip_commands" From 9cee3f13a1ef14c00b5ff4c2bdbaed65a619646b Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 19 May 2016 14:11:27 +0200 Subject: [PATCH 275/363] Implement src:dest for git push completion This allows specifying a local branch to push to a certain remote branch. Fixes #3035. --- share/completions/git.fish | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index 1bdec5cc0..3f2a3b1ce 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -439,10 +439,12 @@ complete -f -c git -n '__fish_git_using_command pull; and __fish_git_branch_for_ ### push complete -f -c git -n '__fish_git_needs_command' -a push -d 'Update remote refs along with associated objects' complete -f -c git -n '__fish_git_using_command push; and not __fish_git_branch_for_remote' -a '(__fish_git_remotes)' -d 'Remote alias' -# The "refspec" here is an optional "+" to signify a force-push (implemented) -# then src:dest (where both src and dest are git objects, so we most likely want to complete branches (not implemented) complete -f -c git -n '__fish_git_using_command push; and __fish_git_branch_for_remote' -a '(__fish_git_branches)' -d 'Branch' +# The "refspec" here is an optional "+" to signify a force-push complete -f -c git -n '__fish_git_using_command push; and __fish_git_branch_for_remote; and string match -q "+*" -- (commandline -ct)' -a '+(__fish_git_branches)' -d 'Force-push branch' +# then src:dest (where both src and dest are git objects, so we want to complete branches) +complete -f -c git -n '__fish_git_using_command push; and __fish_git_branch_for_remote; and string match -q "+*:*" -- (commandline -ct)' -a '+(__fish_git_branches):(__fish_git_branch_for_remote)' -d 'Force-push local branch to remote branch' +complete -f -c git -n '__fish_git_using_command push; and __fish_git_branch_for_remote; and string match -q "*:*" -- (commandline -ct)' -a '(__fish_git_branches):(__fish_git_branch_for_remote)' -d 'Push local branch to remote branch' complete -f -c git -n '__fish_git_using_command push' -l all -d 'Push all refs under refs/heads/' complete -f -c git -n '__fish_git_using_command push' -l prune -d "Remove remote branches that don't have a local counterpart" complete -f -c git -n '__fish_git_using_command push' -l mirror -d 'Push all refs under refs/' From 4d63ebde15567a0af57835b4e12058c3fc0040e6 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 19 May 2016 14:29:04 +0200 Subject: [PATCH 276/363] Remove stray "=" from completions. --- share/completions/busctl.fish | 4 +-- share/completions/flac.fish | 36 +++++++++++++------------- share/completions/nm.fish | 6 ++--- share/completions/oggenc.fish | 12 ++++----- share/completions/rsync.fish | 22 ++++++++-------- share/completions/systemctl.fish | 4 +-- share/completions/systemd-analyze.fish | 4 +-- 7 files changed, 44 insertions(+), 44 deletions(-) diff --git a/share/completions/busctl.fish b/share/completions/busctl.fish index ea3ef2765..695c30355 100644 --- a/share/completions/busctl.fish +++ b/share/completions/busctl.fish @@ -141,8 +141,8 @@ complete -f -c busctl -n "__fish_seen_subcommand_from list" -l show-machine -d ' complete -x -c busctl -n "__fish_seen_subcommand_from list status" -l augment-creds -a "yes no" complete -f -c busctl -l user complete -f -c busctl -l system -complete -f -c busctl -s H -l host= -a "(__fish_print_hostnames)" -complete -f -c busctl -s M -l machine= -a "(__fish_systemd_machines)" +complete -f -c busctl -s H -l host -a "(__fish_print_hostnames)" +complete -f -c busctl -s M -l machine -a "(__fish_systemd_machines)" complete -f -c busctl -l no-pager complete -f -c busctl -l no-legend complete -f -c busctl -s h -l help diff --git a/share/completions/flac.fish b/share/completions/flac.fish index 2754a1f2e..c7818bc7b 100644 --- a/share/completions/flac.fish +++ b/share/completions/flac.fish @@ -11,26 +11,26 @@ complete -c flac -s c -l stdout -d "Write output to stdout" complete -c flac -s s -l silent -d "Do not write runtime encode/decode statistics" complete -c flac -l totally-silent -d "Do not print anything including errors" complete -c flac -s f -l force -d "Force overwriting of output files" -complete -c flac -r -s o -l output-name= -d "Force the output file name" -complete -c flac -l output-prefix= -d "Prepend STRING to output names" +complete -c flac -r -s o -l output-name -d "Force the output file name" +complete -c flac -l output-prefix -d "Prepend STRING to output names" complete -c flac -l delete-input-file -d "Deletes after a successful encode/decode" -complete -c flac -l skip= -d "Skip the given initial samples for each input {#|mm:ss.ss}" -complete -c flac -l until= -d "Stop at the given sample for each input file {#|[+|-]mm:ss.ss}" +complete -c flac -l skip -d "Skip the given initial samples for each input {#|mm:ss.ss}" +complete -c flac -l until -d "Stop at the given sample for each input file {#|[+|-]mm:ss.ss}" complete -c flac -l ogg -d "Use Ogg as transport layer" complete -c flac -l serial-number -d "Serial number to use for the FLAC stream" complete -c flac -l residual-text -d "Include residual signal in text output" complete -c flac -l residual-gnuplot -d "Generate gnuplot files of residual distribution" complete -c flac -s F -l decode-through-errors -d "Continue decoding through stream errors" -complete -c flac -l cue= -d "Set the beginning and ending cuepoints to decode [#.#][-[#.#]]" +complete -c flac -l cue -d "Set the beginning and ending cuepoints to decode [#.#][-[#.#]]" complete -c flac -s V -l verify -d "Verify a correct encoding" complete -c flac -l lax -d "Allow encoder to generate non-Subset files" complete -c flac -l sector-align -d "Align multiple files on sector boundaries" complete -c flac -l replay-gain -d "Calculate ReplayGain & store in Vorbis comments" -complete -c flac -l cuesheet= -d "Import cuesheet and store in CUESHEET block" -complete -c flac -x -s T -l tag= -d "Add a Vorbis comment FIELD=VALUE; may appear multiple times" -complete -c flac -x -s T -l tag-from-file= -d "Read tags from file" -complete -c flac -x -s S -l seekpoint= -d "Add seek point(s) {#|X|#x|#s}" -complete -c flac -x -s P -l padding= -d "Write a PADDING block of length #" +complete -c flac -l cuesheet -d "Import cuesheet and store in CUESHEET block" +complete -c flac -x -s T -l tag -d "Add a Vorbis comment FIELD=VALUE; may appear multiple times" +complete -c flac -x -s T -l tag-from-file -d "Read tags from file" +complete -c flac -x -s S -l seekpoint -d "Add seek point(s) {#|X|#x|#s}" +complete -c flac -x -s P -l padding -d "Write a PADDING block of length #" complete -c flac -s 0 -l compression-level-0 -d "Synonymous with -l 0 -b 1152 -r 22" complete -c flac -l fast -d "Synonymous with -l 0 -b 1152 -r 22" complete -c flac -s 1 -l compression-level-1 -d "Synonymous with -l 0 -b 1152 -M -r 2,2" @@ -42,23 +42,23 @@ complete -c flac -s 6 -l compression-level-6 -d "Synonymous with -l 8 -b complete -c flac -s 7 -l compression-level-7 -d "Synonymous with -l 8 -b 4608 -m -e -r 6" complete -c flac -s 8 -l compression-level-8 -d "Synonymous with -l 12 -b 4608 -m -e -r 6" complete -c flac -l best -d "Synonymous with -l 12 -b 4608 -m -e -r 6" -complete -c flac -x -s b -l blocksize= -d "Specify blocksize in samples" +complete -c flac -x -s b -l blocksize -d "Specify blocksize in samples" complete -c flac -s m -l mid-side -d "Try mid-side coding for each frame" complete -c flac -s M -l adaptive-mid-side -d "Adaptive mid-side coding for all frames" complete -c flac -s e -l exhaustive-model-search -d "Do exhaustive model search (expensive!)" -complete -c flac -x -s l -l max-lpc-order= -d "Max LPC order; 0 => only fixed predictors" +complete -c flac -x -s l -l max-lpc-order -d "Max LPC order; 0 => only fixed predictors" complete -c flac -s p -l qlp-coeff-precision-search -d "Exhaustively search LP coeff quantization" -complete -c flac -x -s q -l qlp-coeff-precision= -d "Specify precision in bits" -complete -c flac -x -s r -l rice-partition-order= -d "Set [min,]max residual partition order" +complete -c flac -x -s q -l qlp-coeff-precision -d "Specify precision in bits" +complete -c flac -x -s r -l rice-partition-order -d "Set [min,]max residual partition order" complete -c flac -l endian=big -d "Set byte order for samples" complete -c flac -l endian=little -d "Set byte order for samples" -complete -c flac -l channels= -d "Number of channels" -complete -c flac -l bps= -d "Number of bits per sample" -complete -c flac -l sample-rate= -d "Sample rate in Hz" +complete -c flac -l channels -d "Number of channels" +complete -c flac -l bps -d "Number of bits per sample" +complete -c flac -l sample-rate -d "Sample rate in Hz" complete -c flac -l sign=unsigned -d "Sign of samples" complete -c flac -l sign=signed -d "Sign of samples" -complete -c flac -l input-size= -d "Size of the raw input in bytes" +complete -c flac -l input-size -d "Size of the raw input in bytes" complete -c flac -l force-aiff-format -d "Force decoding to AIFF format" complete -c flac -l force-raw-format -d "Treat input or output as raw samples" diff --git a/share/completions/nm.fish b/share/completions/nm.fish index 832d31925..eb9909817 100644 --- a/share/completions/nm.fish +++ b/share/completions/nm.fish @@ -7,7 +7,7 @@ complete -c nm -s A -l print-file-name -d 'Print name of the input file before e complete -c nm -l no-demangle -d 'Do not demangle low-level symbol names' complete -c nm -s D -l dynamic -d 'Display dynamic symbols instead of normal symbols' complete -c nm -l defined-only -d 'Display only defined symbols' -complete -c nm -s f -l format=FORMAT -d 'Use the output format FORMAT. FORMAT can be "bsd", "sysv" or "posix". The default is "bsd"' +complete -c nm -s f -l format -d 'Use the output format FORMAT. The default is "bsd"' -a 'bsd sysv posix' complete -c nm -s g -l extern-only -d 'Display only external symbols' complete -c nm -s l -l line-numbers -d 'Use debugging information to find a filename and line number for each symbol' complete -c nm -s n -l numeric-sort -d 'Sort symbols numerically by address' @@ -21,8 +21,8 @@ complete -c nm -s s -l print-armap -d 'Include index for symbols from arch complete -c nm -l size-sort -d 'Sort symbols by size' complete -c nm -l special-syms -d 'Include special symbols in the output' complete -c nm -l synthetic -d 'Display synthetic symbols as well' -complete -c nm -s t -l radix=RADIX -d 'Use RADIX for printing symbol values' -complete -c nm -l target=BFDNAME -d 'Specify the target object format as BFDNAME' +complete -c nm -s t -l radix -d 'Use RADIX for printing symbol values' +complete -c nm -l target -d 'Specify the target object format as BFDNAME' complete -c nm -s u -l undefined-only -d 'Display only undefined symbols' complete -c nm -s h -l help -d 'Display this information' complete -c nm -s V -l version -d "Display this program's version number" diff --git a/share/completions/oggenc.fish b/share/completions/oggenc.fish index 7d06a8526..2c0a46465 100644 --- a/share/completions/oggenc.fish +++ b/share/completions/oggenc.fish @@ -5,9 +5,9 @@ complete -c oggenc -s Q -l quiet -f -d "Produce no output to stderr" complete -c oggenc -s h -l help -f -d "Print this help text" complete -c oggenc -s v -l version -f -d "Print the version number" complete -c oggenc -s r -l raw -f -d "Raw mode. Input files are read directly as PCM data" -complete -c oggenc -s B -l raw-bits= -x -d "Set bits/sample for raw input. Default is 16" -complete -c oggenc -s C -l raw-chan= -x -d "Set number of channels for raw input" -complete -c oggenc -s R -l raw-rate= -x -d "Set samples/sec for raw input" +complete -c oggenc -s B -l raw-bits -x -d "Set bits/sample for raw input. Default is 16" +complete -c oggenc -s C -l raw-chan -x -d "Set number of channels for raw input" +complete -c oggenc -s R -l raw-rate -x -d "Set samples/sec for raw input" complete -c oggenc -l raw-endianness -f -d "1 for bigendian, 0 for little (defaults to 0)" complete -c oggenc -s b -l bitrate -x -d "Choose a nominal bitrate to encode at" complete -c oggenc -l managed -f -d "Enable the bitrate management engine" @@ -19,11 +19,11 @@ complete -c oggenc -l resample -x -d "Resample input data to sampling rate complete -c oggenc -l downmix -f -d "Downmix stereo to mono" complete -c oggenc -s s -l serial -x -d "Specify a serial number for the stream" complete -c oggenc -l discard-comments -f -d "Prevents comments in FLAC and Ogg FLAC files from being copied to the output Ogg Vorbis file" -complete -c oggenc -s o -l output= -x -d "Write file to fn (only valid in single-file mode)" -complete -c oggenc -s n -l names= -x -d "Produce filenames as this string" +complete -c oggenc -s o -l output -x -d "Write file to fn (only valid in single-file mode)" +complete -c oggenc -s n -l names -x -d "Produce filenames as this string" complete -c oggenc -s X -l name-remove= -x -d "Remove the specified characters from parameters" complete -c oggenc -s P -l name-replace= -x -d "Replace characters removed by --name-remove" -complete -c oggenc -s c -l comment= -x -d "Add the given string as an extra comment" +complete -c oggenc -s c -l comment -x -d "Add the given string as an extra comment" complete -c oggenc -s d -l date -x -d "Date for track" complete -c oggenc -s N -l tracknum -x -d "Track number" complete -c oggenc -s t -l title -x -d "Title of track" diff --git a/share/completions/rsync.fish b/share/completions/rsync.fish index 040c332a0..26bf00908 100644 --- a/share/completions/rsync.fish +++ b/share/completions/rsync.fish @@ -8,8 +8,8 @@ complete -c rsync -s r -l recursive --description "Recurse into directories" complete -c rsync -s R -l relative --description "Use relative path names" complete -c rsync -l no-implied-dirs --description "Don’t send implied dirs with --relative" complete -c rsync -s b -l backup --description "Make backups (see --suffix & --backup-dir)" -complete -c rsync -l backup-dir=DIR --description "Make backups into hierarchy based in DIR" -complete -c rsync -l suffix=SUFFIX --description "Backup suffix (default ~ w/o --backup-dir)" +complete -c rsync -l backup-dir --description "Make backups into hierarchy based in DIR" +complete -c rsync -l suffix --description "Backup suffix (default ~ w/o --backup-dir)" complete -c rsync -s u -l update --description "Skip files that are newer on the receiver" complete -c rsync -l inplace --description "Update destination files in-place" complete -c rsync -l append --description "Append data onto shorter files" @@ -25,7 +25,7 @@ complete -c rsync -s p -l perms --description "Preserve permissions" complete -c rsync -s E -l executability --description "Preserve executability" complete -c rsync -s A -l acls --description "Preserve ACLs (implies -p) [non-standard]" complete -c rsync -s X -l xattrs --description "Preserve extended attrs (implies -p) [n.s.]" -complete -c rsync -l chmod=CHMOD --description "Change destination permissions" +complete -c rsync -l chmod --description "Change destination permissions" complete -c rsync -s o -l owner --description "Preserve owner (super-user only)" complete -c rsync -s g -l group --description "Preserve group" complete -c rsync -l devices --description "Preserve device files (super-user only)" @@ -60,7 +60,7 @@ complete -c rsync -l partial-dir=DIR --description "Put a partially transferred complete -c rsync -l delay-updates --description "Put all updated files into place at end" complete -c rsync -s m -l prune-empty-dirs --description "Prune empty directory chains from file-list" complete -c rsync -l numeric-ids --description "Don’t map uid/gid values by user/group name" -complete -c rsync -l timeout=TIME --description "Set I/O timeout in seconds" +complete -c rsync -l timeout --description "Set I/O timeout in seconds" complete -c rsync -s I -l ignore-times --description "Don’t skip files that match size and time" complete -c rsync -l size-only --description "Skip files that match in size" complete -c rsync -l modify-window=NUM --description "Compare mod-times with reduced accuracy" @@ -72,17 +72,17 @@ complete -c rsync -l link-dest=DIR --description "Hardlink to files in DIR when complete -c rsync -s z -l compress --description "Compress file data during the transfer" complete -c rsync -l compress-level --description "Explicitly set compression level" complete -c rsync -s C -l cvs-exclude --description "Auto-ignore files in the same way CVS does" -complete -c rsync -s f -l filter=RULE --description "Add a file-filtering RULE" +complete -c rsync -s f -l filter --description "Add a file-filtering RULE" complete -c rsync -s F --description "Same as --filter=’dir-merge /.rsync-filter’ repeated: --filter='- .rsync-filter'" -complete -c rsync -l exclude=PATTERN --description "Exclude files matching PATTERN" -complete -c rsync -l exclude-from=FILE --description "Read exclude patterns from FILE" -complete -c rsync -l include=PATTERN --description "Don’t exclude files matching PATTERN" +complete -c rsync -l exclude --description "Exclude files matching PATTERN" +complete -c rsync -l exclude-from --description "Read exclude patterns from FILE" +complete -c rsync -l include --description "Don’t exclude files matching PATTERN" complete -c rsync -l include-from=FILE --description "Read include patterns from FILE" complete -c rsync -l files-from=FILE --description "Read list of source-file names from FILE" complete -c rsync -s 0 -l from0 --description "All *from/filter files are delimited by 0s" -complete -c rsync -l address=ADDRESS --description "Bind address for outgoing socket to daemon" -complete -c rsync -l port=PORT --description "Specify double-colon alternate port number" -complete -c rsync -l sockopts=OPTIONS --description "Specify custom TCP options" +complete -c rsync -l address --description "Bind address for outgoing socket to daemon" +complete -c rsync -l port --description "Specify double-colon alternate port number" +complete -c rsync -l sockopts --description "Specify custom TCP options" complete -c rsync -l blocking-io --description "Use blocking I/O for the remote shell" complete -c rsync -l stats --description "Give some file-transfer stats" complete -c rsync -s 8 -l 8-bit-output --description "Leave high-bit chars unescaped in output" diff --git a/share/completions/systemctl.fish b/share/completions/systemctl.fish index 296eeeb33..8f8116db7 100644 --- a/share/completions/systemctl.fish +++ b/share/completions/systemctl.fish @@ -93,8 +93,8 @@ complete -f -c systemctl -l runtime -d 'Make changes only temporarily' complete -f -r -c systemctl -s n -l lines -d 'Number of journal lines to show' -a "(seq 1 1000)" complete -f -c systemctl -s o -l output -d 'Control journal formatting' -xa 'short short-monotonic verbose export json json-pretty json-sse cat' complete -f -c systemctl -l plain -d 'list-dependencies flat, not as tree' -complete -f -c systemctl -s H -l host= -d 'Execute the operation on a remote host' -a "(__fish_print_hostnames)" -complete -x -c systemctl -s M -l machine= -d 'Execute operation on a VM or container' -a "(__fish_systemd_machines)" +complete -f -c systemctl -s H -l host -d 'Execute the operation on a remote host' -a "(__fish_print_hostnames)" +complete -x -c systemctl -s M -l machine -d 'Execute operation on a VM or container' -a "(__fish_systemd_machines)" complete -f -c systemctl -s h -l help -d 'Print a short help and exit' complete -f -c systemctl -l version -d 'Print a short version and exit' complete -f -c systemctl -l no-pager -d 'Do not pipe output into a pager' diff --git a/share/completions/systemd-analyze.fish b/share/completions/systemd-analyze.fish index 52448dccc..268f6aa0c 100644 --- a/share/completions/systemd-analyze.fish +++ b/share/completions/systemd-analyze.fish @@ -1,6 +1,6 @@ complete -c systemd-analyze -x -complete -f -c systemd-analzye -s H -l host= -d 'Execute the operation on a remote host' -a "(__fish_print_hostnames)" -complete -x -c systemd-analzye -s M -l machine= -d 'Execute operation on a VM or container' -a "(__fish_systemd_machines)" +complete -f -c systemd-analzye -s H -l host -d 'Execute the operation on a remote host' -a "(__fish_print_hostnames)" +complete -x -c systemd-analzye -s M -l machine -d 'Execute operation on a VM or container' -a "(__fish_systemd_machines)" complete -f -c systemd-analzye -s h -l help -d 'Print a short help and exit' complete -f -c systemd-analzye -l version -d 'Print a short version and exit' complete -f -c systemd-analzye -l no-pager -d 'Do not pipe output into a pager' From a0b3b8ac4c010e47197c7d4a4a9627337255707e Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 19 May 2016 14:50:25 +0200 Subject: [PATCH 277/363] Add networkctl completion Very simple, but effective. --- share/completions/networkctl.fish | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 share/completions/networkctl.fish diff --git a/share/completions/networkctl.fish b/share/completions/networkctl.fish new file mode 100644 index 000000000..370fb626c --- /dev/null +++ b/share/completions/networkctl.fish @@ -0,0 +1,5 @@ +set -l cmds status list lldp + +complete -c networkctl -f -n '__fish_seen_subcommand_from status' -a '(networkctl list --no-pager --no-legend -a | string trim \ +| string replace -r \'([0-9]+) (\w+) .*$\' \'$2\t$1\n$1\t$2\')' +complete -c networkctl -x -n "not __fish_seen_subcommand_from $cmds" -a "$cmds" From 573b3797a507c3912197ebd4f2df0dc0398d3826 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 19 May 2016 14:52:38 +0200 Subject: [PATCH 278/363] Improve asp's package completion --- share/completions/asp.fish | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/share/completions/asp.fish b/share/completions/asp.fish index b95b914dc..8308f235f 100644 --- a/share/completions/asp.fish +++ b/share/completions/asp.fish @@ -17,6 +17,10 @@ complete -c asp -n "not __fish_seen_subcommand_from $commands" -a show -d "Show complete -c asp -n "not __fish_seen_subcommand_from $commands" -a update -d "Update given targets" -f complete -c asp -n "not __fish_seen_subcommand_from $commands" -a untrack -d "Remove target from local repository" -f -# This isn't perfect as we need a pkgbase, not a pkgname, -# but getting those is non-trivial as built packages don't carry the information anymore -complete -c asp -n "__fish_seen_subcommand_from $commands" -a "(__fish_print_packages)" -f +# Remove pointless "packages/" or "community/" before package names +# Don't show foreign packages for untrack, and show no packages at all for gc, help, disk-usage, list-{all,local} +# This will run into the description race. +complete -c asp -n "__fish_seen_subcommand_from checkout {diff,short,}log export list-{arches,repos} show update" -a "(asp list-all | string replace -r '.*/' '')" -f +complete -c asp -n "__fish_seen_subcommand_from checkout {diff,short,}log export list-{arches,repos} show update untrack" -a "(asp list-local | string replace -r '.*/' '')" -f \ +-d "Locally tracked package" + From 30ea7cc3f8a5d56ad30dc749ea374363c15f312a Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Thu, 19 May 2016 13:00:40 -0700 Subject: [PATCH 279/363] Update docs to reflect new if/while condtion chaining Documents new behavior in #1428 --- doc_src/and.txt | 7 ++++--- doc_src/if.txt | 7 +++---- doc_src/or.txt | 8 ++++---- doc_src/while.txt | 9 ++++----- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/doc_src/and.txt b/doc_src/and.txt index 0b4f681d1..ce09285f1 100644 --- a/doc_src/and.txt +++ b/doc_src/and.txt @@ -7,11 +7,12 @@ COMMAND1; and COMMAND2 \subsection and-description Description -`and` is used to execute a command if the current exit status (as set by the last previous command) is 0. +`and` is used to execute a command if the current exit status (as set by the previous command) is 0. -`and` does not change the current exit status. +`and` statements may be used as part of the condition in an `and` or `while` block. See the documentation +for `if` and `while` for examples. -The exit status of the last foreground command to exit can always be accessed using the $status variable. +`and` does not change the current exit status. The exit status of the last foreground command to exit can always be accessed using the $status variable. \subsection and-example Example diff --git a/doc_src/if.txt b/doc_src/if.txt index 1cecdcd35..a30b10110 100644 --- a/doc_src/if.txt +++ b/doc_src/if.txt @@ -12,7 +12,7 @@ end `if` will execute the command `CONDITION`. If the condition's exit status is 0, the commands `COMMANDS_TRUE` will execute. If the exit status is not 0 and `else` is given, `COMMANDS_FALSE` will be executed. -In order to use the exit status of multiple commands as the condition of an if block, use `begin; ...; end` and the short circuit commands `and` and `or`. +You can use `and` or `or` in the condition. See the second example below. The exit status of the last foreground command to exit can always be accessed using the $status variable. @@ -33,9 +33,8 @@ end The following code will print "foo.txt exists and is readable" if foo.txt is a regular file and readable \fish -if begin test -f foo.txt - and test -r foo.txt - end +if test -f foo.txt + and test -r foo.txt echo "foo.txt exists and is readable" end \endfish diff --git a/doc_src/or.txt b/doc_src/or.txt index 1cd1d768e..97707e064 100644 --- a/doc_src/or.txt +++ b/doc_src/or.txt @@ -7,12 +7,12 @@ COMMAND1; or COMMAND2 \subsection or-description Description -`or` is used to execute a command if the current exit status (as set by the last previous command) is not 0. +`or` is used to execute a command if the current exit status (as set by the previous command) is not 0. -`or` does not change the current exit status. - -The exit status of the last foreground command to exit can always be accessed using the $status variable. +`or` statements may be used as part of the condition in an `and` or `while` block. See the documentation +for `if` and `while` for examples. +`or` does not change the current exit status. The exit status of the last foreground command to exit can always be accessed using the $status variable. \subsection or-example Example diff --git a/doc_src/while.txt b/doc_src/while.txt index c06874d59..855edce11 100644 --- a/doc_src/while.txt +++ b/doc_src/while.txt @@ -11,12 +11,11 @@ while CONDITION; COMMANDS...; end If the exit status of `CONDITION` is non-zero on the first iteration, `COMMANDS` will not be executed at all. -Use `begin; ...; end` for complex conditions; more complex control can be achieved with `while true` containing a break. - +You can use `and` or `or` for complex conditions. Even more complex control can be achieved with `while true` containing a break. \subsection while-example Example \fish -while test -f foo.txt; echo file exists; sleep 10; end -# outputs 'file exists' at 10 second intervals as long as the file foo.txt exists. -\endfish \ No newline at end of file +while test -f foo.txt; or test -f bar.txt ; echo file exists; sleep 10; end +# outputs 'file exists' at 10 second intervals as long as the file foo.txt or bar.txt exists. +\endfish From ade6ebf5226e67aa5d5fa64ad56921d69f03f4e9 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Thu, 19 May 2016 13:00:40 -0700 Subject: [PATCH 280/363] Update docs to reflect new if/while condtion chaining Documents new behavior in #1428 Cherry picked from 30ea7cc3f8a5d56ad30dc749ea374363c15f312a --- doc_src/and.txt | 7 ++++--- doc_src/if.txt | 7 +++---- doc_src/or.txt | 8 ++++---- doc_src/while.txt | 9 ++++----- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/doc_src/and.txt b/doc_src/and.txt index 0b4f681d1..ce09285f1 100644 --- a/doc_src/and.txt +++ b/doc_src/and.txt @@ -7,11 +7,12 @@ COMMAND1; and COMMAND2 \subsection and-description Description -`and` is used to execute a command if the current exit status (as set by the last previous command) is 0. +`and` is used to execute a command if the current exit status (as set by the previous command) is 0. -`and` does not change the current exit status. +`and` statements may be used as part of the condition in an `and` or `while` block. See the documentation +for `if` and `while` for examples. -The exit status of the last foreground command to exit can always be accessed using the $status variable. +`and` does not change the current exit status. The exit status of the last foreground command to exit can always be accessed using the $status variable. \subsection and-example Example diff --git a/doc_src/if.txt b/doc_src/if.txt index 1cecdcd35..a30b10110 100644 --- a/doc_src/if.txt +++ b/doc_src/if.txt @@ -12,7 +12,7 @@ end `if` will execute the command `CONDITION`. If the condition's exit status is 0, the commands `COMMANDS_TRUE` will execute. If the exit status is not 0 and `else` is given, `COMMANDS_FALSE` will be executed. -In order to use the exit status of multiple commands as the condition of an if block, use `begin; ...; end` and the short circuit commands `and` and `or`. +You can use `and` or `or` in the condition. See the second example below. The exit status of the last foreground command to exit can always be accessed using the $status variable. @@ -33,9 +33,8 @@ end The following code will print "foo.txt exists and is readable" if foo.txt is a regular file and readable \fish -if begin test -f foo.txt - and test -r foo.txt - end +if test -f foo.txt + and test -r foo.txt echo "foo.txt exists and is readable" end \endfish diff --git a/doc_src/or.txt b/doc_src/or.txt index 1cd1d768e..97707e064 100644 --- a/doc_src/or.txt +++ b/doc_src/or.txt @@ -7,12 +7,12 @@ COMMAND1; or COMMAND2 \subsection or-description Description -`or` is used to execute a command if the current exit status (as set by the last previous command) is not 0. +`or` is used to execute a command if the current exit status (as set by the previous command) is not 0. -`or` does not change the current exit status. - -The exit status of the last foreground command to exit can always be accessed using the $status variable. +`or` statements may be used as part of the condition in an `and` or `while` block. See the documentation +for `if` and `while` for examples. +`or` does not change the current exit status. The exit status of the last foreground command to exit can always be accessed using the $status variable. \subsection or-example Example diff --git a/doc_src/while.txt b/doc_src/while.txt index c06874d59..855edce11 100644 --- a/doc_src/while.txt +++ b/doc_src/while.txt @@ -11,12 +11,11 @@ while CONDITION; COMMANDS...; end If the exit status of `CONDITION` is non-zero on the first iteration, `COMMANDS` will not be executed at all. -Use `begin; ...; end` for complex conditions; more complex control can be achieved with `while true` containing a break. - +You can use `and` or `or` for complex conditions. Even more complex control can be achieved with `while true` containing a break. \subsection while-example Example \fish -while test -f foo.txt; echo file exists; sleep 10; end -# outputs 'file exists' at 10 second intervals as long as the file foo.txt exists. -\endfish \ No newline at end of file +while test -f foo.txt; or test -f bar.txt ; echo file exists; sleep 10; end +# outputs 'file exists' at 10 second intervals as long as the file foo.txt or bar.txt exists. +\endfish From 46be5ac468db923bd3d19c55638b723aa760cd4e Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Wed, 18 May 2016 17:46:13 -0700 Subject: [PATCH 281/363] make fish buildable on OS X Snow Leopard I noticed that the `test_convert()` function was randomly failing when run on OS X Snow Leopard. I tracked it down to the `mbrtowc()` function on that OS being broken. Explicitly testing for UTF-8 prefixes that identify a sequence longer than four bytes (which the Unicode standard made illegal long ago) keeps us from having encoding errors on those OS's. This also makes the errors reported by the `test_convert()` function actually useful and readable. Lastly, it makes it possible to build fish on OS X Snow Leopard. --- src/common.cpp | 48 +++++++++++++++++++++++++++++----------------- src/fallback.cpp | 11 ++++++++--- src/fallback.h | 9 ++++++++- src/fish_tests.cpp | 24 +++++++++++++++++++---- src/wutil.cpp | 6 ++++++ 5 files changed, 72 insertions(+), 26 deletions(-) diff --git a/src/common.cpp b/src/common.cpp index 7bd909158..b8270ad1f 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -109,8 +109,12 @@ void __attribute__((noinline)) show_stackframe(const wchar_t msg_level, int fram int skip_levels) { ASSERT_IS_NOT_FORKED_CHILD(); + // TODO: Decide if this is still needed. I'm commenting it out because it caused me some grief + // while trying to debug a test failure. And the tests run just fine without spurious failures + // if this check is not done. + // // Hack to avoid showing backtraces in the tester. - if (program_name && !wcscmp(program_name, L"(ignore)")) return; + // if (program_name && !wcscmp(program_name, L"(ignore)")) return; if (frame_count < 1) frame_count = 999; debug_shared(msg_level, L"Backtrace:"); @@ -177,24 +181,32 @@ static wcstring str2wcs_internal(const char *in, const size_t in_len) { mbstate_t state = {}; while (in_pos < in_len) { - wchar_t wc = 0; - size_t ret = mbrtowc(&wc, &in[in_pos], in_len - in_pos, &state); - - // Determine whether to encode this characters with our crazy scheme. bool use_encode_direct = false; - if (wc >= ENCODE_DIRECT_BASE && wc < ENCODE_DIRECT_BASE + 256) { - use_encode_direct = true; - } else if (wc == INTERNAL_SEPARATOR) { - use_encode_direct = true; - } else if (ret == (size_t)-2) { - // Incomplete sequence. - use_encode_direct = true; - } else if (ret == (size_t)-1) { - // Invalid data. - use_encode_direct = true; - } else if (ret > in_len - in_pos) { - // Other error codes? Terrifying, should never happen. + size_t ret; + wchar_t wc = 0; + + if ((in[in_pos] & 0xF8) == 0xF8) { + // Protect against broken mbrtowc() implementations which attempt to encode UTF-8 + // sequences longer than four bytes (e.g., OS X Snow Leopard). use_encode_direct = true; + } else { + ret = mbrtowc(&wc, &in[in_pos], in_len - in_pos, &state); + + // Determine whether to encode this characters with our crazy scheme. + if (wc >= ENCODE_DIRECT_BASE && wc < ENCODE_DIRECT_BASE + 256) { + use_encode_direct = true; + } else if (wc == INTERNAL_SEPARATOR) { + use_encode_direct = true; + } else if (ret == (size_t)-2) { + // Incomplete sequence. + use_encode_direct = true; + } else if (ret == (size_t)-1) { + // Invalid data. + use_encode_direct = true; + } else if (ret > in_len - in_pos) { + // Other error codes? Terrifying, should never happen. + use_encode_direct = true; + } } if (use_encode_direct) { @@ -221,7 +233,7 @@ wcstring str2wcstring(const char *in, size_t len) { return str2wcs_internal(in, wcstring str2wcstring(const char *in) { return str2wcs_internal(in, strlen(in)); } wcstring str2wcstring(const std::string &in) { - /* Handles embedded nulls! */ + // Handles embedded nulls! return str2wcs_internal(in.data(), in.size()); } diff --git a/src/fallback.cpp b/src/fallback.cpp index 7a8291247..96fa417b0 100644 --- a/src/fallback.cpp +++ b/src/fallback.cpp @@ -131,7 +131,8 @@ __attribute__((unused)) static int wcsncasecmp_fallback(const wchar_t *a, const return wcsncasecmp_fallback(a + 1, b + 1, count - 1); } -#if __APPLE__ && __DARWIN_C_LEVEL >= 200809L +#if __APPLE__ +#if __DARWIN_C_LEVEL >= 200809L // Note parens avoid the macro expansion. wchar_t *wcsdup_use_weak(const wchar_t *a) { if (&wcsdup != NULL) return (wcsdup)(a); @@ -147,8 +148,12 @@ int wcsncasecmp_use_weak(const wchar_t *s1, const wchar_t *s2, size_t n) { if (&wcsncasecmp != NULL) return (wcsncasecmp)(s1, s2, n); return wcsncasecmp_fallback(s1, s2, n); } - -#endif //__APPLE__ +#else // __DARWIN_C_LEVEL >= 200809L +wchar_t *wcsdup(const wchar_t *in) { return wcsdup_fallback(in); } +int wcscasecmp(const wchar_t *a, const wchar_t *b) { return wcscasecmp_fallback(a, b); } +int wcsncasecmp(const wchar_t *a, const wchar_t *b, size_t n) { return wcsncasecmp_fallback(a, b, n); } +#endif // __DARWIN_C_LEVEL >= 200809L +#endif // __APPLE__ #ifndef HAVE_WCSNDUP wchar_t *wcsndup(const wchar_t *in, size_t c) { diff --git a/src/fallback.h b/src/fallback.h index b28e7b179..a3143f097 100644 --- a/src/fallback.h +++ b/src/fallback.h @@ -62,13 +62,20 @@ char *tparm_solaris_kludge(char *str, ...); /// these functions only exist on 10.7+. /// /// On other platforms, use what's detected at build time. -#if __APPLE__ && __DARWIN_C_LEVEL >= 200809L +#if __APPLE__ +#if __DARWIN_C_LEVEL >= 200809L wchar_t *wcsdup_use_weak(const wchar_t *); int wcscasecmp_use_weak(const wchar_t *, const wchar_t *); int wcsncasecmp_use_weak(const wchar_t *s1, const wchar_t *s2, size_t n); #define wcsdup(a) wcsdup_use_weak((a)) #define wcscasecmp(a, b) wcscasecmp_use_weak((a), (b)) #define wcsncasecmp(a, b, c) wcsncasecmp_use_weak((a), (b), (c)) +#else +wchar_t *wcsdup(const wchar_t *in); +int wcscasecmp(const wchar_t *a, const wchar_t *b); +int wcsncasecmp(const wchar_t *s1, const wchar_t *s2, size_t n); +wchar_t *wcsndup(const wchar_t *in, size_t c); +#endif #endif //__APPLE__ #ifndef HAVE_WCSNDUP diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 7f1c0a7d6..2457cee49 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -258,6 +258,18 @@ static void test_format(void) { do_test(!strcmp(buff1, buff2)); } +/// Helper to convert a narrow string to a sequence of hex digits. +static char *str2hex(const char *input) { + char *output = (char *)malloc(5 * strlen(input) + 1); + char *p = output; + for (; *input; input++) { + sprintf(p, "0x%02X ", (int)*input & 0xFF); + p += 5; + } + *p = '\0'; + return output; +} + /// Test wide/narrow conversion by creating random strings and verifying that the original string /// comes back thorugh double conversion. static void test_convert() { @@ -318,8 +330,13 @@ static void test_convert() { } if (strcmp(o, n)) { - err(L"Line %d - %d: Conversion cycle of string %s produced different string %s", - __LINE__, i, o, n); + char *o2 = str2hex(o); + char *n2 = str2hex(n); + err(L"Line %d - %d: Conversion cycle of string:\n%4d chars: %s\n" + L"produced different string:\n%4d chars: %s", + __LINE__, i, strlen(o), o2, strlen(n), n2); + free(o2); + free(n2); } free((void *)n); } @@ -3882,8 +3899,7 @@ int main(int argc, char **argv) { } } - setlocale(LC_ALL, ""); - // srand(time(0)); + srand(time(0)); configure_thread_assertions_for_testing(); program_name = L"(ignore)"; diff --git a/src/wutil.cpp b/src/wutil.cpp index 4299956aa..9f975fab4 100644 --- a/src/wutil.cpp +++ b/src/wutil.cpp @@ -343,7 +343,13 @@ wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path) { res = wcsdup(wide_res.c_str()); } +#if __APPLE__ && __DARWIN_C_LEVEL < 200809L + // OS X Snow Leopard is broken with respect to the dynamically allocated buffer returned by + // realpath(). It's not dynamically allocated so attempting to free that buffer triggers a + // malloc/free error. Thus we don't attempt the free in this case. +#else free(narrow_res); +#endif return res; } From 7c243694549cc1d6776873c8a97c99fc5889b68d Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Thu, 19 May 2016 19:27:22 -0700 Subject: [PATCH 282/363] fix building on Cygwin Cygwin still doesn't support any of the backtrace functions. Also, remove a spurious newline from a debug message. Fixes #2993 --- configure.ac | 4 +--- src/common.cpp | 11 ++++++++++- src/exec.cpp | 2 +- src/fallback.cpp | 8 -------- src/fallback.h | 8 -------- 5 files changed, 12 insertions(+), 21 deletions(-) diff --git a/configure.ac b/configure.ac index 3600ed96e..035ca4f69 100644 --- a/configure.ac +++ b/configure.ac @@ -217,7 +217,6 @@ CXXFLAGS="$CXXFLAGS -Wall -Wno-sign-compare" # # This is needed in order to get the really cool backtraces on Linux # - AC_MSG_CHECKING([for -rdynamic linker flag]) prev_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -rdynamic" @@ -268,7 +267,6 @@ AC_SEARCH_LIBS( shm_open, rt, , [AC_MSG_ERROR([Cannot find the rt library, neede AC_SEARCH_LIBS( pthread_create, pthread, , [AC_MSG_ERROR([Cannot find the pthread library, needed to build this package.] )] ) AC_SEARCH_LIBS( setupterm, [ncurses tinfo curses], , [AC_MSG_ERROR([Could not find a curses implementation, needed to build fish. If this is Linux, try running 'sudo apt-get install libncurses5-dev' or 'sudo yum install ncurses-devel'])] ) AC_SEARCH_LIBS( [nan], [m], [AC_DEFINE( [HAVE_NAN], [1], [Define to 1 if you have the nan function])] ) -AC_SEARCH_LIBS( [backtrace_symbols_fd], [execinfo] ) AC_SEARCH_LIBS( [dladdr], [dl] ) if test x$local_gettext != xno; then @@ -313,7 +311,7 @@ AC_STRUCT_DIRENT_D_TYPE AC_CHECK_FUNCS( wcsndup ) AC_CHECK_FUNCS( futimes ) AC_CHECK_FUNCS( wcslcat wcslcpy lrand48_r killpg ) -AC_CHECK_FUNCS( backtrace backtrace_symbols_fd getifaddrs ) +AC_CHECK_FUNCS( backtrace_symbols getifaddrs ) AC_CHECK_FUNCS( futimens clock_gettime ) AC_CHECK_DECL( [mkostemp], [ AC_CHECK_FUNCS([mkostemp]) ] ) diff --git a/src/common.cpp b/src/common.cpp index b8270ad1f..91b08824c 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -70,7 +70,9 @@ static volatile bool termsize_valid; static rwlock_t termsize_rwlock; static char *wcs2str_internal(const wchar_t *in, char *out); +static void debug_shared(const wchar_t msg_level, const wcstring &msg); +#ifdef HAVE_BACKTRACE_SYMBOLS // This function produces a stack backtrace with demangled function & method names. It is based on // https://gist.github.com/fmela/591333 but adapted to the style of the fish project. static const wcstring_list_t __attribute__((noinline)) @@ -104,7 +106,6 @@ demangled_backtrace(int max_frames, int skip_levels) { return backtrace_text; } -static void debug_shared(const wchar_t msg_level, const wcstring &msg); void __attribute__((noinline)) show_stackframe(const wchar_t msg_level, int frame_count, int skip_levels) { ASSERT_IS_NOT_FORKED_CHILD(); @@ -124,6 +125,14 @@ void __attribute__((noinline)) show_stackframe(const wchar_t msg_level, int fram } } +#else // HAVE_BACKTRACE_SYMBOLS + +void __attribute__((noinline)) show_stackframe(const wchar_t msg_level, int frame_count, + int skip_levels) { + debug_shared(msg_level, L"Sorry, but your system does not support backtraces"); +} +#endif // HAVE_BACKTRACE_SYMBOLS + int fgetws2(wcstring *s, FILE *f) { int i = 0; wint_t c; diff --git a/src/exec.cpp b/src/exec.cpp index afd09f6aa..aefdbc12d 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -1058,7 +1058,7 @@ void exec_job(parser_t &parser, job_t *j) { // 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. - debug(2, L"Fork #%d, pid %d: spawn external command '%s' from '%ls'\n", + debug(2, L"Fork #%d, pid %d: spawn external command '%s' from '%ls'", g_fork_count, pid, actual_cmd, file ? file : L""); if (pid == 0) { job_mark_process_as_failed(j, p); diff --git a/src/fallback.cpp b/src/fallback.cpp index 96fa417b0..dc5bcce2f 100644 --- a/src/fallback.cpp +++ b/src/fallback.cpp @@ -332,14 +332,6 @@ int killpg(int pgr, int sig) { } #endif -#ifndef HAVE_BACKTRACE -int backtrace(void **buffer, int size) { return 0; } -#endif - -#ifndef HAVE_BACKTRACE_SYMBOLS_FD -char **backtrace_symbols_fd(void *const *buffer, int size, int fd) { return 0; } -#endif - #ifndef HAVE_NAN double nan(char *tagp) { return 0.0 / 0.0; } #endif diff --git a/src/fallback.h b/src/fallback.h index a3143f097..f167ff69f 100644 --- a/src/fallback.h +++ b/src/fallback.h @@ -149,12 +149,4 @@ int killpg(int pgr, int sig); double nan(char *tagp); #endif -#ifndef HAVE_BACKTRACE -int backtrace(void **buffer, int size); -#endif - -#ifndef HAVE_BACKTRACE_SYMBOLS_FD -char **backtrace_symbols_fd(void *const *buffer, int size, int fd); -#endif - #endif From 432c0058a9d39412038556fe4d43f9240e5c85bb Mon Sep 17 00:00:00 2001 From: James Campos Date: Fri, 20 May 2016 06:57:29 -0700 Subject: [PATCH 283/363] [doc] move regex example (#3045) this example uses regex, so it should not be in the glob examples --- doc_src/string.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc_src/string.txt b/doc_src/string.txt index 31677858e..2306678a0 100644 --- a/doc_src/string.txt +++ b/doc_src/string.txt @@ -121,6 +121,12 @@ The following subcommands are available: >_ echo 'ok?' | string match '*\\?' >_ \outp{ok?} +\subsection string-example-match-regex Match Regex Examples + +\fish{cli-dark} +>_ string match -r 'cat|dog|fish' 'nice dog' +\outp{dog} + >_ string match -r -v "c.*[12]" {cat,dog}(seq 1 4) \outp{dog1} \outp{dog2} @@ -130,12 +136,6 @@ The following subcommands are available: \outp{dog4} \endfish -\subsection string-example-match-regex Match Regex Examples - -\fish{cli-dark} ->_ string match -r 'cat|dog|fish' 'nice dog' -\outp{dog} - >_ string match -r '(\\d\\d?):(\\d\\d):(\\d\\d)' \asis{2:34:56} \outp{2:34:56} \outp{2} From 14b953e414caf2d52df47cdfffa8cbf3b94ae65d Mon Sep 17 00:00:00 2001 From: Jorge Bucaran Date: Sun, 1 May 2016 18:58:43 +0900 Subject: [PATCH 284/363] Add missing color definitions to __fish_init_1_50_0 reset. (#2987) * Add missing color definitions to __fish_init_1_50_0 reset. The values where determined by inspecting the values of: * fish_color_end * fish_color_user * fish_color_host after resetting the color theme via fish_config. * Add documentation for fish_color_user and fish_color_host. (cherry picked from commit 08c29727e01a541f54f9161e08cbb10020d9d456) --- doc_src/index.hdr.in | 4 ++++ share/functions/__fish_config_interactive.fish | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index 58b13bb6b..c907486d5 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -900,6 +900,10 @@ The following variables are available to change the highlighting colors in fish: - `fish_color_autosuggestion`, the color used for autosuggestions +- `fish_color_user`, the color used to print the current username in some of fish default prompts + +- `fish_color_host`, the color used to print the current host system in some of fish default prompts + Additionally, the following variables are available to change the highlighting in the completion pager: - `fish_pager_color_prefix`, the color of the prefix string, i.e. the string that is to be completed diff --git a/share/functions/__fish_config_interactive.fish b/share/functions/__fish_config_interactive.fish index bca410ac5..f8b08de62 100644 --- a/share/functions/__fish_config_interactive.fish +++ b/share/functions/__fish_config_interactive.fish @@ -46,8 +46,12 @@ function __fish_config_interactive -d "Initializations that should be performed set -q fish_color_error; or set -U fish_color_error red --bold set -q fish_color_escape; or set -U fish_color_escape cyan set -q fish_color_operator; or set -U fish_color_operator cyan + set -q fish_color_end; or set -U fish_color_end green set -q fish_color_quote; or set -U fish_color_quote brown set -q fish_color_autosuggestion; or set -U fish_color_autosuggestion 555 yellow + set -q fish_color_user; or set -U fish_color_user green + + set -q fish_color_host; or set -U fish_color_host normal set -q fish_color_valid_path; or set -U fish_color_valid_path --underline set -q fish_color_cwd; or set -U fish_color_cwd green From 245be2c2e4b0f53af39f3db5ad340bf0a0e98a76 Mon Sep 17 00:00:00 2001 From: David Adam Date: Fri, 20 May 2016 22:37:30 +0000 Subject: [PATCH 285/363] CHANGELOG: update for 2.3.0 --- CHANGELOG.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d7e72724..d3189e8d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +# fish 2.3.0 (released May 20, 2016) + +There are no significant changes between 2.3.0 and 2.3b2. + +## Other notable fixes and improvements + +- `abbr` now allows non-letter keys (#2996). +- Define a few extra colours on first start (#2987). +- Multiple documentation updates. +- Added completions for rmmod (#3007). +- Improved completions for git (#2998). + +## Known issues + +- Interactive commands started from fish configuration files or from the `-c` option may, under certain circumstances, be started with incorrect terminal modes and fail to behave as expected. A fix is planned but requires further testing (#2619). + +--- + # fish 2.3b2 (released May 5, 2016) ## Significant changes @@ -12,6 +30,8 @@ - Avoid confusing the terminal line driver with non-printing characters in `fish_title` (#2453). - Improved completions for busctl, git (#2585, #2879, #2984), and netctl. +--- + # fish 2.3b1 (released April 19, 2016) ## Significant Changes @@ -67,6 +87,8 @@ - PWD shortening in the prompt can now be configured via the `fish_prompt_pwd_dir_length` variable, set to the length per path component (#2473) - fish no longer requires `/etc/fish/config.fish` to correctly start, and now ships a skeleton file that only contains some documentation (#2799) +--- + # fish 2.2.0 (released July 12, 2015) ### Significant changes ### From 7828f2303a87594092ec1a7b5407c363b49108d3 Mon Sep 17 00:00:00 2001 From: David Adam Date: Fri, 20 May 2016 22:44:47 +0000 Subject: [PATCH 286/363] osx/config.h: update to match current configure output --- osx/config.h | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/osx/config.h b/osx/config.h index a2b4e7a61..b5d86e053 100644 --- a/osx/config.h +++ b/osx/config.h @@ -248,17 +248,49 @@ /* Perform string translations with gettext */ /* #undef USE_GETTEXT */ +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif + + /* The size of wchar_t in bits. */ #define WCHAR_T_BITS 32 +/* Define to 1 if on MINIX. */ +/* #undef _MINIX */ + /* Macro to enable additional prototypes under BSD */ /* #undef _NETBSD_SOURCE */ +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +/* #undef _POSIX_1_SOURCE */ + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +/* #undef _POSIX_SOURCE */ + /* Macro to enable additional prototypes under BSD */ /* #undef __BSD_VISIBLE */ /* Macro to enable additional prototypes under Solaris */ -/* #undef __EXTENSIONS__ */ +#define __EXTENSIONS__ 1 #if __GNUC__ >= 3 #ifndef __warn_unused From 216a45edee502c081dccfd8be85d27ab125fe829 Mon Sep 17 00:00:00 2001 From: David Adam Date: Fri, 20 May 2016 22:48:00 +0000 Subject: [PATCH 287/363] Bump version for VERSION --- osx/Info.plist | 2 +- osx/config.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osx/Info.plist b/osx/Info.plist index d977fe4da..c07543b40 100644 --- a/osx/Info.plist +++ b/osx/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 2.2.991 + 2.3.0 CFBundleVersion 0.1 LSApplicationCategoryType diff --git a/osx/config.h b/osx/config.h index b5d86e053..19ce8b773 100644 --- a/osx/config.h +++ b/osx/config.h @@ -222,7 +222,7 @@ #define PACKAGE_NAME "fish" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "fish 2.3b2" +#define PACKAGE_STRING "fish 2.3.0" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "fish" @@ -231,7 +231,7 @@ #define PACKAGE_URL "" /* Define to the version of this package. */ -#define PACKAGE_VERSION "2.3b2" +#define PACKAGE_VERSION "2.3.0" /* The size of `wchar_t', as computed by sizeof. */ #define SIZEOF_WCHAR_T 4 From 60317190bd9c0abab21986f3e528f5c7359f87a6 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 22 May 2016 19:54:11 +0200 Subject: [PATCH 288/363] Check validity of fish_key_bindings This potentially leads to an unusable session (when fish_key_bindings is set in config.fish to a value without corresponding function), so we should take care. --- .../functions/__fish_config_interactive.fish | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/share/functions/__fish_config_interactive.fish b/share/functions/__fish_config_interactive.fish index ec891ddd2..5d634e778 100644 --- a/share/functions/__fish_config_interactive.fish +++ b/share/functions/__fish_config_interactive.fish @@ -158,25 +158,41 @@ function __fish_config_interactive -d "Initializations that should be performed # do nothing if the key bindings didn't actually change # This could be because the variable was set to the existing value # or because it was a local variable - if test "$fish_key_bindings" = "$__fish_active_key_bindings" + # If fish_key_bindings is empty on the first run, we still need to set the defaults + if test "$fish_key_bindings" = "$__fish_active_key_bindings" -a -n "$fish_key_bindings" return end + # Check if fish_key_bindings is a valid function + # If not, either keep the previous bindings (if any) or revert to default + # Also print an error so the user knows + if not functions -q "$fish_key_bindings" + echo "There is no fish_key_bindings function called: '$fish_key_bindings'" >&2 + if set -q __fish_active_key_bindings + echo "Keeping $__fish_active_key_bindings" >&2 + return 1 + else + echo "Reverting to default bindings" >&2 + set fish_key_bindings fish_default_key_bindings + # Return because we are called again + return 0 + end + end set -g __fish_active_key_bindings "$fish_key_bindings" set -g fish_bind_mode default if test "$fish_key_bindings" = fish_default_key_bindings - fish_default_key_bindings + # Redirect stderr per #1155 + fish_default_key_bindings ^/dev/null else eval $fish_key_bindings ^/dev/null end # Load user key bindings if they are defined if functions --query fish_user_key_bindings >/dev/null - fish_user_key_bindings + fish_user_key_bindings ^/dev/null end end - # Load key bindings. Redirect stderr per #1155 - set -g __fish_active_key_bindings - __fish_reload_key_bindings ^/dev/null + # Load key bindings + __fish_reload_key_bindings # Repaint screen when window changes size function __fish_winch_handler --on-signal WINCH From 5accc7c6c5b2234998d1a88de2dfeb1057bd5507 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Mon, 23 May 2016 00:49:09 +0200 Subject: [PATCH 289/363] Fix funced's tmpfile generation on OSX OSX mktemp... isn't great, so work around that fact. --- share/functions/funced.fish | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/share/functions/funced.fish b/share/functions/funced.fish index 6439e53fa..8f62e96bc 100644 --- a/share/functions/funced.fish +++ b/share/functions/funced.fish @@ -87,7 +87,11 @@ function funced --description 'Edit function definition' return 0 end - set tmpname (mktemp -t fish_funced.XXXXXXXXXX.fish) + # OSX mktemp is rather restricted - no suffix, no way to automatically use TMPDIR + # Create a directory so we can use a ".fish" suffix for the file - makes editors pick up that it's a fish file + set -q TMPDIR; or set -l TMPDIR /tmp + set -l tmpdir (mktemp -d $TMPDIR/fish.XXXXXX) + set -l tmpname $tmpdir/$funcname.fish if functions -q -- $funcname functions -- $funcname > $tmpname @@ -120,6 +124,6 @@ function funced --description 'Edit function definition' break end set -l stat $status - rm -f $tmpname >/dev/null + rm -rf $tmpdir >/dev/null return $stat end From 85e701f42212fb51ba260d98555dc2763360ed83 Mon Sep 17 00:00:00 2001 From: David Adam Date: Sun, 22 May 2016 22:54:00 +0000 Subject: [PATCH 290/363] build_tools: drop make_deb and description-pak [ci skip] --- build_tools/description-pak | 3 --- build_tools/make_deb.sh | 18 ------------------ 2 files changed, 21 deletions(-) delete mode 100644 build_tools/description-pak delete mode 100755 build_tools/make_deb.sh diff --git a/build_tools/description-pak b/build_tools/description-pak deleted file mode 100644 index 029bc12ed..000000000 --- a/build_tools/description-pak +++ /dev/null @@ -1,3 +0,0 @@ -This is the_ridiculous'fish s delightful fork of, fish friendly interactive shell. For more information, visit http://ridiculousfish.com/shell/ . - -This installer will install fish, but will not modify your /etc/shells file or your default shell. I trust you know how to do that yourself if you care to! diff --git a/build_tools/make_deb.sh b/build_tools/make_deb.sh deleted file mode 100755 index 1bf09f0ae..000000000 --- a/build_tools/make_deb.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/sh - -# Terminate on error -set -e - -sudo rm -Rf /tmp/fishfish -mkdir /tmp/fishfish -git archive --format=tar fish_fish | tar -x -C /tmp/fishfish -mkdir /tmp/fishfish/doc-pak -cp README INSTALL CHANGELOG release_notes.html /tmp/fishfish/doc-pak/ -cp build_tools/description-pak /tmp/fishfish/ -cd /tmp/fishfish -autoconf -./configure -make -j 3 -sudo checkinstall --default --pakdir ~/fish_built/ --pkgversion 0.9 make install -mv ~/fish_built/fishfish_0.9-1_i386.deb ~/fish_built/fishfish_0.9_i386.deb - From 2606cfe72db59735e8ab685f778968a1cf8c10be Mon Sep 17 00:00:00 2001 From: Camille Scholtz Date: Wed, 18 May 2016 17:37:13 +0200 Subject: [PATCH 291/363] add option to modify script being restyled This change allows the user to specify the script name on the CLI in addition to being redirected from stdin. It also adds a `-w` flag to write the modified script to the original file. --- doc_src/fish_indent.txt | 4 ++- src/fish_indent.cpp | 74 +++++++++++++++++++++++++++++++---------- 2 files changed, 60 insertions(+), 18 deletions(-) diff --git a/doc_src/fish_indent.txt b/doc_src/fish_indent.txt index b4cf13c0d..5f38449ba 100644 --- a/doc_src/fish_indent.txt +++ b/doc_src/fish_indent.txt @@ -7,12 +7,14 @@ fish_indent [OPTIONS] \subsection fish_indent-description Description -`fish_indent` is used to indent a piece of fish code. `fish_indent` reads commands from standard input and outputs them to standard output. +`fish_indent` is used to indent a piece of fish code. `fish_indent` reads commands from standard input and outputs them to standard output or a specified file. The following options are available: - `-d` or `--dump` dumps information about the parsed fish commands to stderr +- `-w` or `--write` indents a specified file and immediately writes to that file + - `-i` or `--no-indent` do not indent commands; only reformat to one job per line - `-v` or `--version` displays the current fish version and then exits diff --git a/src/fish_indent.cpp b/src/fish_indent.cpp index 7e68e3101..a1479f479 100644 --- a/src/fish_indent.cpp +++ b/src/fish_indent.cpp @@ -18,12 +18,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA #include "config.h" // IWYU pragma: keep #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -164,10 +166,9 @@ static void prettify_node_recursive(const wcstring &source, const parse_node_tre // Entry point for prettification. static wcstring prettify(const wcstring &src, bool do_indent) { parse_node_tree_t tree; - if (!parse_tree_from_string(src, - parse_flag_continue_after_error | parse_flag_include_comments | - parse_flag_leave_unterminated | parse_flag_show_blank_lines, - &tree, NULL /* errors */)) { + int parse_flags = (parse_flag_continue_after_error | parse_flag_include_comments | + parse_flag_leave_unterminated | parse_flag_show_blank_lines); + if (!parse_tree_from_string(src, parse_flags, &tree, NULL)) { // We return the initial string on failure. return src; } @@ -339,26 +340,27 @@ int main(int argc, char *argv[]) { // Types of output we support. enum { output_type_plain_text, + output_type_file, output_type_ansi, output_type_html } output_type = output_type_plain_text; + const char *output_location; bool do_indent = true; - const char *short_opts = "+dhvi"; - const struct option long_opts[] = {{"dump", no_argument, NULL, 'd'}, - {"no-indent", no_argument, NULL, 'i'}, - {"help", no_argument, NULL, 'h'}, - {"version", no_argument, NULL, 'v'}, - {"html", no_argument, NULL, 1}, - {"ansi", no_argument, NULL, 2}, - {NULL, 0, NULL, 0}}; + const char *short_opts = "+dhvwi"; + const struct option long_opts[] = { + {"dump", no_argument, NULL, 'd'}, {"no-indent", no_argument, NULL, 'i'}, + {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'v'}, + {"write", no_argument, NULL, 'w'}, {"html", no_argument, NULL, 1}, + {"ansi", no_argument, NULL, 2}, {NULL, 0, NULL, 0}}; int opt; while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) { switch (opt) { case 0: { fwprintf(stderr, _(L"getopt_long() unexpectedly returned zero\n")); - exit_without_destructors(127); + exit(127); + break; } case 'd': { dump_parse_tree = true; @@ -366,12 +368,16 @@ int main(int argc, char *argv[]) { } case 'h': { print_help("fish_indent", 1); - exit_without_destructors(0); + exit(0); + break; } case 'v': { fwprintf(stderr, _(L"%ls, version %s\n"), program_name, get_fish_version()); exit(0); - assert(0 && "Unreachable code reached"); + break; + } + case 'w': { + output_type = output_type_file; break; } case 'i': { @@ -388,12 +394,33 @@ int main(int argc, char *argv[]) { } default: { // We assume getopt_long() has already emitted a diagnostic msg. - exit_without_destructors(1); + exit(1); + break; } } } - const wcstring src = read_file(stdin); + argc -= optind; + argv += optind; + + wcstring src; + if (argc == 0) { + src = read_file(stdin); + } else if (argc == 1) { + FILE *fh = fopen(*argv, "r"); + if (fh) { + src = read_file(fh); + fclose(fh); + output_location = *argv; + } else { + fwprintf(stderr, _(L"Opening \"%s\" failed: %s\n"), *argv, strerror(errno)); + exit(1); + } + } else { + fwprintf(stderr, _(L"Too many arguments\n")); + exit(1); + } + const wcstring output_wtext = prettify(src, do_indent); // Maybe colorize. @@ -409,6 +436,19 @@ int main(int argc, char *argv[]) { colored_output = no_colorize(output_wtext); break; } + case output_type_file: { + FILE *fh = fopen(output_location, "w"); + if (fh) { + fputs(wcs2str(output_wtext), fh); + fclose(fh); + exit(0); + } else { + fwprintf(stderr, _(L"Opening \"%s\" failed: %s\n"), output_location, + strerror(errno)); + exit(1); + } + break; + } case output_type_ansi: { colored_output = ansi_colorize(output_wtext, colors); break; From 49d9883b3e296ed2b0ca493c21b777cd12c0e0d9 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sun, 22 May 2016 20:21:04 -0700 Subject: [PATCH 292/363] disable the oclint InvertedLogic rule --- .oclint | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.oclint b/.oclint index f38f47e67..ad70da646 100644 --- a/.oclint +++ b/.oclint @@ -36,3 +36,8 @@ disable-rules: # a lot of really annoying warnings, we're going to disable this rule. # - ShortVariableName + # + # This rule flags perfectly reasonable conditions like `if (!some_condition)` + # and is therefore just noise. Disable this rule. + # + - InvertedLogic From dc470bcad3905a19ce8e72ddf2c6c063ad99e450 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Mon, 23 May 2016 11:29:06 +0200 Subject: [PATCH 293/363] Document noclobber redirections Fixes #2812. --- doc_src/index.hdr.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index ff0141360..9eb620a7a 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -146,6 +146,8 @@ An example of a file redirection is `echo hello > output.txt`, which directs the - To append standard output to a file, write `>>DESTINATION_FILE` - To append standard error to a file, write `^^DESTINATION_FILE` +- To not overwrite ("clobber") an existing file, write '>?DESTINATION' or '^?DESTINATION' + `DESTINATION` can be one of the following: - A filename. The output will be written to the specified file. From 309e10e7a2e9455f8e80898e290865a5d49ae1bd Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 19 May 2016 16:48:45 +0200 Subject: [PATCH 294/363] Don't mangle arguments in abbr This now (rightly) throws an error if there's a space in the key (because we can't store it). Fixes #2997. --- share/functions/abbr.fish | 49 +++++++++++++++++---------------------- tests/abbr.err | 1 + tests/abbr.in | 2 ++ 3 files changed, 24 insertions(+), 28 deletions(-) diff --git a/share/functions/abbr.fish b/share/functions/abbr.fish index 8606f84b2..dc4f42638 100644 --- a/share/functions/abbr.fish +++ b/share/functions/abbr.fish @@ -8,8 +8,8 @@ function abbr --description "Manage abbreviations" if test $needs_arg = single set mode_arg $argv[1] set needs_arg no - else if test $needs_arg = coalesce - set mode_arg "$argv" + else if test $needs_arg = multi + set mode_arg $argv set needs_arg no set -e argv else @@ -20,7 +20,7 @@ function abbr --description "Manage abbreviations" return 0 case '-a' '--add' set new_mode add - set needs_arg coalesce + set needs_arg multi case '-e' '--erase' set new_mode erase set needs_arg single @@ -57,7 +57,7 @@ function abbr --description "Manage abbreviations" if test -z "$mode" if set -q argv[1] set mode 'add' - set mode_arg "$argv" + set mode_arg $argv set -e argv else set mode 'show' @@ -72,24 +72,23 @@ function abbr --description "Manage abbreviations" switch $mode case 'add' - # Convert from old "key=value" to new "key value" syntax - if string match -qr '^[^ ]+=' -- $mode_arg - set mode_arg (string replace "=" " " -- $mode_arg) + # Convert from old "key=value" syntax + # TODO: This should be removed later + if not set -q mode_arg[2]; and string match -qr '^[^ ]+=' -- $mode_arg + set mode_arg (string split "=" -- $mode_arg) end # Bail out early if the exact abbr is already in - contains -- $mode_arg $fish_user_abbreviations; and return 0 - set -l key - set -l value - set -l kv (__fish_abbr_split $mode_arg) - set key $kv[1] - set value $kv[2] - # ensure the key contains at least one non-space character - if not string match -qr "[^\s]" -- $key - printf ( _ "%s: abbreviation must have a non-empty key\n" ) abbr >&2 + contains -- "$mode_arg" $fish_user_abbreviations; and return 0 + set -l key $mode_arg[1] + set -e mode_arg[1] + set -l value "$mode_arg" + # Because we later store "$key $value", there can't be any spaces in the key + if string match -q "* *" -- $key + printf ( _ "%s: abbreviation cannot have spaces in the key\n" ) abbr >&2 return 1 end - if not string match -qr "\w" -- $value + if test -z "$value" printf ( _ "%s: abbreviation must have a value\n" ) abbr >&2 return 1 end @@ -102,22 +101,21 @@ function abbr --description "Manage abbreviations" # and therefore work properly if someone sets this as a global variable set -U fish_user_abbreviations end - set fish_user_abbreviations $fish_user_abbreviations $mode_arg + set fish_user_abbreviations $fish_user_abbreviations "$key $value" return 0 case 'erase' - set -l key (__fish_abbr_split $mode_arg)[1] - if set -l idx (__fish_abbr_get_by_key $key) + if set -l idx (__fish_abbr_get_by_key $mode_arg) set -e fish_user_abbreviations[$idx] return 0 else - printf ( _ "%s: no such abbreviation '%s'\n" ) abbr $key >&2 + printf ( _ "%s: no such abbreviation '%s'\n" ) abbr $mode_arg >&2 return 2 end case 'show' for i in $fish_user_abbreviations - set -l kv (__fish_abbr_split $i) + set -l kv (string split " " -m 1 -- $i) set -l key $kv[1] set -l value $kv[2] @@ -130,7 +128,7 @@ function abbr --description "Manage abbreviations" case 'list' for i in $fish_user_abbreviations - set -l key (__fish_abbr_split $i)[1] + set -l key (string split " " -m 1 -- $i)[1] printf "%s\n" $key end return 0 @@ -154,8 +152,3 @@ function __fish_abbr_get_by_key end return 1 end - -function __fish_abbr_split -a input - # Because we always save space-separated, we can be certain that this will match - string split " " -m 1 -- $input -end diff --git a/tests/abbr.err b/tests/abbr.err index 904a0fd41..13fdfd748 100644 --- a/tests/abbr.err +++ b/tests/abbr.err @@ -1 +1,2 @@ abbr: no such abbreviation 'NOT_AN_ABBR' +abbr: abbreviation cannot have spaces in the key diff --git a/tests/abbr.in b/tests/abbr.in index be828b08a..b4f870494 100644 --- a/tests/abbr.in +++ b/tests/abbr.in @@ -35,3 +35,5 @@ abbr -e '--__abbr3' # Ensure we are not recognizing later "=" as separators abbr d2 env a=b banana abbr -l | string match -q d2; or echo "= test failed" + +abbr "a b c" "d e f"; or true From c238ad35bdd065dc9bda13ad840f1b47c325971b Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 19 May 2016 17:48:39 +0200 Subject: [PATCH 295/363] Fix "--" argument in abbr --- share/functions/abbr.fish | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/share/functions/abbr.fish b/share/functions/abbr.fish index dc4f42638..6f1df7e6e 100644 --- a/share/functions/abbr.fish +++ b/share/functions/abbr.fish @@ -5,16 +5,8 @@ function abbr --description "Manage abbreviations" set -l mode_arg set -l needs_arg no while set -q argv[1] - if test $needs_arg = single - set mode_arg $argv[1] - set needs_arg no - else if test $needs_arg = multi - set mode_arg $argv - set needs_arg no - set -e argv - else - set -l new_mode - switch $argv[1] + set -l new_mode + switch $argv[1] case '-h' '--help' __fish_print_help abbr return 0 @@ -36,17 +28,26 @@ function abbr --description "Manage abbreviations" return 1 case '*' break - end - if test -n "$mode" -a -n "$new_mode" - # we're trying to set two different modes - printf ( _ "%s: %s cannot be specified along with %s\n" ) abbr $argv[1] $mode_flag >&2 - return 1 - end - set mode $new_mode - set mode_flag $argv[1] end + if test -n "$mode" -a -n "$new_mode" + # we're trying to set two different modes + printf ( _ "%s: %s cannot be specified along with %s\n" ) abbr $argv[1] $mode_flag >&2 + return 1 + end + set mode $new_mode + set mode_flag $argv[1] set -e argv[1] end + + if test $needs_arg = single + set mode_arg $argv[1] + set needs_arg no + set -e argv[1] + else if test $needs_arg = multi + set mode_arg $argv + set needs_arg no + set -e argv + end if test $needs_arg != no printf ( _ "%s: option requires an argument -- %s\n" ) abbr $mode_flag >&2 return 1 From 46f4819ffade061d202973996e4974562a45ecd0 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 19 May 2016 17:49:09 +0200 Subject: [PATCH 296/363] Fix printing "--" in abbr --show --- share/functions/abbr.fish | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/share/functions/abbr.fish b/share/functions/abbr.fish index 6f1df7e6e..beaeda30b 100644 --- a/share/functions/abbr.fish +++ b/share/functions/abbr.fish @@ -116,13 +116,14 @@ function abbr --description "Manage abbreviations" case 'show' for i in $fish_user_abbreviations + set -l opt_double_dash set -l kv (string split " " -m 1 -- $i) set -l key $kv[1] set -l value $kv[2] # Check to see if either key or value has a leading dash # If so, we need to write -- - string match -q -- '-*' $key $value; and set -l opt_double_dash '--' + string match -q -- '-*' $key $value; and set opt_double_dash '--' echo abbr $opt_double_dash (string escape -- $key $value) end return 0 From dac8483f7e96ac9d18152c571ba7ed48f2341f4d Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 19 May 2016 17:54:15 +0200 Subject: [PATCH 297/363] Simplify some code in abbr We actually need less duplication here. --- share/functions/abbr.fish | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/share/functions/abbr.fish b/share/functions/abbr.fish index beaeda30b..cdb528aef 100644 --- a/share/functions/abbr.fish +++ b/share/functions/abbr.fish @@ -39,6 +39,17 @@ function abbr --description "Manage abbreviations" set -e argv[1] end + # If run with no options, treat it like --add if we have an argument, or + # --show if we do not have an argument + if not set -q mode[1] + if set -q argv[1] + set mode add + set needs_arg multi + else + set mode show + end + end + if test $needs_arg = single set mode_arg $argv[1] set needs_arg no @@ -53,18 +64,6 @@ function abbr --description "Manage abbreviations" return 1 end - # If run with no options, treat it like --add if we have an argument, or - # --show if we do not have an argument - if test -z "$mode" - if set -q argv[1] - set mode 'add' - set mode_arg $argv - set -e argv - else - set mode 'show' - end - end - # none of our modes want any excess arguments if set -q argv[1] printf ( _ "%s: Unexpected argument -- %s\n" ) abbr $argv[1] >&2 From 8b44358c53d9a55a60490ef6d33152079eca5fc2 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Mon, 23 May 2016 20:19:22 +0200 Subject: [PATCH 298/363] Bring abbr test in line with new behavior Previously, `--erase` would not accept any options and wouldn't read "--" as option-separator. Now it does like every other "command", and it could conceivably gain e.g. a "--prefix" option. --- tests/abbr.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/abbr.in b/tests/abbr.in index b4f870494..2086f66cb 100644 --- a/tests/abbr.in +++ b/tests/abbr.in @@ -30,7 +30,7 @@ abbr -e '~__abbr2' # Ensure we handle leading dashes in abbreviation names properly abbr -- '--__abbr3' 'xyz' abbr | grep __abbr3 -abbr -e '--__abbr3' +abbr -e -- '--__abbr3' # Ensure we are not recognizing later "=" as separators abbr d2 env a=b banana From 8fba36b242f6c22673da6577732ff52e4bba09b7 Mon Sep 17 00:00:00 2001 From: David Adam Date: Sun, 22 May 2016 23:11:26 +0000 Subject: [PATCH 299/363] make_tarball: generate SHA-256 hashes, not SHA-1 Closes #3048. --- build_tools/make_tarball.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_tools/make_tarball.sh b/build_tools/make_tarball.sh index 1e24de0f9..3e070c11f 100755 --- a/build_tools/make_tarball.sh +++ b/build_tools/make_tarball.sh @@ -55,4 +55,4 @@ gzip "$path" # Output what we did, and the sha1 hash echo "Tarball written to $path".gz -openssl sha1 "$path".gz +openssl dgst -sha256 "$path".gz From 475439fa0b934b1add793f9584a706b607e35986 Mon Sep 17 00:00:00 2001 From: Dan Underwood Date: Tue, 24 May 2016 00:10:12 +0100 Subject: [PATCH 300/363] Test for Atom Package Manager now passes correctly Move to `string match` syntax from `grep` caused test to see if the Atom Package Manager is installed to always fail. This appears to fix the issue (tested on fish 2.3.0 with apm 1.6.0). --- share/completions/apm.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/completions/apm.fish b/share/completions/apm.fish index f9bf2f97a..40e6794b5 100644 --- a/share/completions/apm.fish +++ b/share/completions/apm.fish @@ -69,7 +69,7 @@ function __fish_apm_list_packages apm list -b end -if apm -h ^| string match -q "Atom Package Manager*" +if apm -h ^| string match -q "*Atom Package Manager*" # Completions for Atom Package Manager ################## From b9848538e37e8176af9029cfe225bddcf93a6c85 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 24 May 2016 13:50:04 +0200 Subject: [PATCH 301/363] Funced: Limit damage when removing tmpfile This will now only forcibly remove _files_, not directories. $tmpdir _should_ be something only we use in /tmp, but mktemp might screw up. --- share/functions/funced.fish | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/share/functions/funced.fish b/share/functions/funced.fish index 8f62e96bc..14cb0d70a 100644 --- a/share/functions/funced.fish +++ b/share/functions/funced.fish @@ -124,6 +124,8 @@ function funced --description 'Edit function definition' break end set -l stat $status - rm -rf $tmpdir >/dev/null + # Only forcibly delete files to limit possible damage is tmpdir is set to something weird + rm -f $tmpname >/dev/null + rm -r $tmpdir >/dev/null return $stat end From 6b92fdd18d39471ef04b23a44877f88ac15a01dd Mon Sep 17 00:00:00 2001 From: David Adam Date: Tue, 24 May 2016 12:24:25 +0000 Subject: [PATCH 302/363] configure: fix CXXFLAGS with system pcre2 cleanups in configure meant that a number of arguably spurious spaces were dropped from the CXXFLAGS, which produced an error without the below. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 035ca4f69..e457cd508 100644 --- a/configure.ac +++ b/configure.ac @@ -544,7 +544,7 @@ if test "x$included_pcre2" != "xyes"; then XLIBS="$LIBS" LIBS="$LIBS "`$PCRE2_CONFIG --libs$WCHAR_T_BITS 2>/dev/null` XCXXFLAGS="$CXXFLAGS" - CXXFLAGS="$CXXFLAGS"`$PCRE2_CONFIG --cflags` + CXXFLAGS="$CXXFLAGS" `$PCRE2_CONFIG --cflags` # cheat a bit here. the exact library is determined by $WCHAR_T_BITS, # and so AC_CHECK_LIB won't work (can't use a variable as library name) From 0a40dcdb44050ff06a89fb2a837db1b01c4dba8c Mon Sep 17 00:00:00 2001 From: Dennis Ideler Date: Wed, 25 May 2016 00:22:44 +0100 Subject: [PATCH 303/363] Fix documentation links to `or` and `and` command --- doc_src/and.txt | 4 +--- doc_src/if.txt | 3 +-- doc_src/while.txt | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/doc_src/and.txt b/doc_src/and.txt index ce09285f1..ed56da62d 100644 --- a/doc_src/and.txt +++ b/doc_src/and.txt @@ -9,12 +9,10 @@ COMMAND1; and COMMAND2 `and` is used to execute a command if the current exit status (as set by the previous command) is 0. -`and` statements may be used as part of the condition in an `and` or `while` block. See the documentation -for `if` and `while` for examples. +`and` statements may be used as part of the condition in an `if` or `while` block. See the documentation for `if` and `while` for examples. `and` does not change the current exit status. The exit status of the last foreground command to exit can always be accessed using the $status variable. - \subsection and-example Example The following code runs the `make` command to build a program. If the build succeeds, `make`'s exit status is 0, and the program is installed. If either step fails, the exit status is 1, and `make clean` is run, which removes the files created by the build process. diff --git a/doc_src/if.txt b/doc_src/if.txt index a30b10110..61ba387c1 100644 --- a/doc_src/if.txt +++ b/doc_src/if.txt @@ -12,11 +12,10 @@ end `if` will execute the command `CONDITION`. If the condition's exit status is 0, the commands `COMMANDS_TRUE` will execute. If the exit status is not 0 and `else` is given, `COMMANDS_FALSE` will be executed. -You can use `and` or `or` in the condition. See the second example below. +You can use `and` or `or` in the condition. See the second example below. The exit status of the last foreground command to exit can always be accessed using the $status variable. - \subsection if-example Example The following code will print `foo.txt exists` if the file foo.txt exists and is a regular file, otherwise it will print `bar.txt exists` if the file bar.txt exists and is a regular file, otherwise it will print `foo.txt and bar.txt do not exist`. diff --git a/doc_src/while.txt b/doc_src/while.txt index 855edce11..d8bd012e4 100644 --- a/doc_src/while.txt +++ b/doc_src/while.txt @@ -11,7 +11,7 @@ while CONDITION; COMMANDS...; end If the exit status of `CONDITION` is non-zero on the first iteration, `COMMANDS` will not be executed at all. -You can use `and` or `or` for complex conditions. Even more complex control can be achieved with `while true` containing a break. +You can use `and` or `or` for complex conditions. Even more complex control can be achieved with `while true` containing a break. \subsection while-example Example From 2f51088bfb950ab0edcc53668c74e7fb6c58613f Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 13 May 2016 15:02:28 +0200 Subject: [PATCH 304/363] kill: Remove xsel integration Overwriting the user's clipboard by default is annoying and contributors don't use it. This is better served via an explicit binding that calls e.g. `xsel`. --- src/kill.cpp | 86 ++-------------------------------------------------- 1 file changed, 2 insertions(+), 84 deletions(-) diff --git a/src/kill.cpp b/src/kill.cpp index 17a10d85b..706b18156 100644 --- a/src/kill.cpp +++ b/src/kill.cpp @@ -1,7 +1,7 @@ // The killring. // // Works like the killring in emacs and readline. The killring is cut and paste with a memory of -// previous cuts. It supports integration with the X clipboard. +// previous cuts. #include "config.h" // IWYU pragma: keep #include @@ -12,8 +12,6 @@ #include #include "common.h" -#include "env.h" -#include "exec.h" #include "fallback.h" // IWYU pragma: keep #include "kill.h" #include "path.h" @@ -22,59 +20,10 @@ typedef std::list kill_list_t; static kill_list_t kill_list; -/// Contents of the X clipboard, at last time we checked it. -static wcstring cut_buffer; - -/// Test if the xsel command is installed. Since this is called often, cache the result. -static int has_xsel() { - static signed char res = -1; - if (res < 0) { - res = !!path_get_path(L"xsel", NULL); - } - - return res; -} - void kill_add(const wcstring &str) { ASSERT_IS_MAIN_THREAD(); if (str.empty()) return; - - wcstring cmd; - wcstring escaped_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. - // - // 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(), ESCAPE_ALL); - 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; - } - - const env_var_t disp_wstr = env_get_string(L"DISPLAY"); - if (!disp_wstr.missing()) { - escaped_str = escape(str.c_str(), ESCAPE_ALL); - cmd.assign(L"echo -n "); - cmd.append(escaped_str); - cmd.append(L" | xsel -i -b"); - } - } - - if (!cmd.empty()) { - if (exec_subshell(cmd, false /* do not apply exit status */) == -1) { - // Do nothing on failure. - } - cut_buffer = escaped_str; - } } /// Remove first match for specified string from circular list. @@ -99,38 +48,7 @@ const wchar_t *kill_yank_rotate() { return kill_list.front().c_str(); } -/// Check the X clipboard. If it has been changed, add the new clipboard contents to the fish -/// killring. -static void kill_check_x_buffer() { - if (!has_xsel()) return; - - const env_var_t disp = env_get_string(L"DISPLAY"); - if (!disp.missing()) { - size_t i; - wcstring cmd = L"xsel -t 500 -b"; - wcstring new_cut_buffer = L""; - wcstring_list_t list; - if (exec_subshell(cmd, list, false /* do not apply exit status */) != -1) { - for (i = 0; i < list.size(); i++) { - wcstring next_line = escape_string(list.at(i), 0); - if (i > 0) new_cut_buffer += L"\\n"; - new_cut_buffer += next_line; - } - - if (new_cut_buffer.size() > 0) { - // The buffer is inserted with backslash escapes, since we don't really like tabs, - // newlines, etc. anyway. - if (cut_buffer != new_cut_buffer) { - cut_buffer = new_cut_buffer; - kill_list.push_front(new_cut_buffer); - } - } - } - } -} - const wchar_t *kill_yank() { - kill_check_x_buffer(); if (kill_list.empty()) { return L""; } @@ -141,4 +59,4 @@ void kill_sanity_check() {} void kill_init() {} -void kill_destroy() { cut_buffer.clear(); } +void kill_destroy() {} From 1bad956633860ff6ea5c0ce709e7e1947b05517f Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 13 May 2016 15:54:53 +0200 Subject: [PATCH 305/363] docs: Remove section about clipboard integration --- doc_src/index.hdr.in | 3 --- 1 file changed, 3 deletions(-) diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index 9eb620a7a..ee7fd8251 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -1062,9 +1062,6 @@ Command mode is also known as normal mode. `fish` uses an Emacs style kill ring for copy and paste functionality. Use @key{Control,K} to cut from the current cursor position to the end of the line. The string that is cut (a.k.a. killed) is inserted into a linked list of kills, called the kill ring. To paste the latest value from the kill ring use @key{Control,Y}. After pasting, use @key{Alt,Y} to rotate to the previous kill. -If the environment variable `DISPLAY` is set and the `xsel` program is installed, `fish` will try to connect to the X Windows server specified by this variable, and use the clipboard on the X server for copying and pasting. - - \subsection history-search Searchable history After a command has been entered, it is inserted at the end of a history list. Any duplicate history items are automatically removed. By pressing the up and down keys, the user can search forwards and backwards in the history. If the current command line is not empty when starting a history search, only the commands containing the string entered into the command line are shown. From 7d1f45e25f8f7c0bdf0af7d96a60b148b8f54dd3 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 24 May 2016 13:17:03 +0200 Subject: [PATCH 306/363] Add clipboard helper functions and bind them \cy copies, \cv pastes. --- share/functions/fish_clipboard_copy.fish | 7 +++++++ share/functions/fish_clipboard_paste.fish | 7 +++++++ share/functions/fish_default_key_bindings.fish | 3 ++- 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 share/functions/fish_clipboard_copy.fish create mode 100644 share/functions/fish_clipboard_paste.fish diff --git a/share/functions/fish_clipboard_copy.fish b/share/functions/fish_clipboard_copy.fish new file mode 100644 index 000000000..a65efc045 --- /dev/null +++ b/share/functions/fish_clipboard_copy.fish @@ -0,0 +1,7 @@ +function fish_clipboard_copy + if type -q pbcopy + commandline | pbcopy + else if type -q xsel + commandline | xsel --clipboard + end +end diff --git a/share/functions/fish_clipboard_paste.fish b/share/functions/fish_clipboard_paste.fish new file mode 100644 index 000000000..bec43c88e --- /dev/null +++ b/share/functions/fish_clipboard_paste.fish @@ -0,0 +1,7 @@ +function fish_clipboard_paste + if type -q pbpaste + commandline -i (pbpaste) + else if type -q xsel + commandline -i (xsel --clipboard) + end +end diff --git a/share/functions/fish_default_key_bindings.fish b/share/functions/fish_default_key_bindings.fish index e54913219..41b252f9b 100644 --- a/share/functions/fish_default_key_bindings.fish +++ b/share/functions/fish_default_key_bindings.fish @@ -18,7 +18,8 @@ function fish_default_key_bindings -d "Default (Emacs-like) key bindings for fis bind $argv \r execute bind $argv \ck kill-line - bind $argv \cy yank + bind $argv \cy fish_clipboard_copy + bind $argv \cv fish_clipboard_paste bind $argv \t complete bind $argv \e\n "commandline -i \n" From 53c506f109aa9f45becd5539030f5eb4fa7eeaf4 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 24 May 2016 13:17:39 +0200 Subject: [PATCH 307/363] Run fish_indent on default_key_bindings --- .../functions/fish_default_key_bindings.fish | 261 +++++++++--------- 1 file changed, 131 insertions(+), 130 deletions(-) diff --git a/share/functions/fish_default_key_bindings.fish b/share/functions/fish_default_key_bindings.fish index 41b252f9b..3a3a7f734 100644 --- a/share/functions/fish_default_key_bindings.fish +++ b/share/functions/fish_default_key_bindings.fish @@ -1,158 +1,159 @@ function fish_default_key_bindings -d "Default (Emacs-like) key bindings for fish" - if not set -q argv[1] - if test "$fish_key_bindings" != "fish_default_key_bindings" - # Allow the user to set the variable universally - set -q fish_key_bindings; or set -g fish_key_bindings - set fish_key_bindings fish_default_key_bindings # This triggers the handler, which calls us again and ensures the user_key_bindings are executed - return - end - # Clear earlier bindings, if any - bind --erase --all - end + if not set -q argv[1] + if test "$fish_key_bindings" != "fish_default_key_bindings" + # Allow the user to set the variable universally + set -q fish_key_bindings + or set -g fish_key_bindings + set fish_key_bindings fish_default_key_bindings # This triggers the handler, which calls us again and ensures the user_key_bindings are executed + return + end + # Clear earlier bindings, if any + bind --erase --all + end - # This is the default binding, i.e. the one used if no other binding matches - bind $argv "" self-insert + # This is the default binding, i.e. the one used if no other binding matches + bind $argv "" self-insert - bind $argv \n execute - bind $argv \r execute + bind $argv \n execute + bind $argv \r execute - bind $argv \ck kill-line + bind $argv \ck kill-line bind $argv \cy fish_clipboard_copy bind $argv \cv fish_clipboard_paste - bind $argv \t complete + bind $argv \t complete - bind $argv \e\n "commandline -i \n" - bind $argv \e\r "commandline -i \n" + bind $argv \e\n "commandline -i \n" + bind $argv \e\r "commandline -i \n" - bind $argv \e\[A up-or-search - bind $argv \e\[B down-or-search - bind $argv -k down down-or-search - bind $argv -k up up-or-search + bind $argv \e\[A up-or-search + bind $argv \e\[B down-or-search + bind $argv -k down down-or-search + bind $argv -k up up-or-search - # Some linux VTs output these (why?) - bind $argv \eOA up-or-search - bind $argv \eOB down-or-search - bind $argv \eOC forward-char - bind $argv \eOD backward-char + # Some linux VTs output these (why?) + bind $argv \eOA up-or-search + bind $argv \eOB down-or-search + bind $argv \eOC forward-char + bind $argv \eOD backward-char - bind $argv \e\[C forward-char - bind $argv \e\[D backward-char - bind $argv -k right forward-char - bind $argv -k left backward-char + bind $argv \e\[C forward-char + bind $argv \e\[D backward-char + bind $argv -k right forward-char + bind $argv -k left backward-char - bind $argv -k dc delete-char - bind $argv -k backspace backward-delete-char - bind $argv \x7f backward-delete-char + bind $argv -k dc delete-char + bind $argv -k backspace backward-delete-char + bind $argv \x7f backward-delete-char - bind $argv \e\[H beginning-of-line - bind $argv \e\[F end-of-line + bind $argv \e\[H beginning-of-line + bind $argv \e\[F end-of-line - # for PuTTY - # https://github.com/fish-shell/fish-shell/issues/180 - bind $argv \e\[1~ beginning-of-line - bind $argv \e\[3~ delete-char - bind $argv \e\[4~ end-of-line + # for PuTTY + # https://github.com/fish-shell/fish-shell/issues/180 + bind $argv \e\[1~ beginning-of-line + bind $argv \e\[3~ delete-char + bind $argv \e\[4~ end-of-line - # OS X SnowLeopard doesn't have these keys. Don't show an annoying error message. - bind $argv -k home beginning-of-line 2> /dev/null - bind $argv -k end end-of-line 2> /dev/null - bind $argv \e\[3\;2~ backward-delete-char # Mavericks Terminal.app shift-delete + # OS X SnowLeopard doesn't have these keys. Don't show an annoying error message. + bind $argv -k home beginning-of-line 2>/dev/null + bind $argv -k end end-of-line 2>/dev/null + bind $argv \e\[3\;2~ backward-delete-char # Mavericks Terminal.app shift-delete - bind $argv \e\eOC nextd-or-forward-word - bind $argv \e\eOD prevd-or-backward-word - bind $argv \e\e\[C nextd-or-forward-word - bind $argv \e\e\[D prevd-or-backward-word - bind $argv \eO3C nextd-or-forward-word - bind $argv \eO3D prevd-or-backward-word - bind $argv \e\[3C nextd-or-forward-word - bind $argv \e\[3D prevd-or-backward-word - bind $argv \e\[1\;3C nextd-or-forward-word - bind $argv \e\[1\;3D prevd-or-backward-word + bind $argv \e\eOC nextd-or-forward-word + bind $argv \e\eOD prevd-or-backward-word + bind $argv \e\e\[C nextd-or-forward-word + bind $argv \e\e\[D prevd-or-backward-word + bind $argv \eO3C nextd-or-forward-word + bind $argv \eO3D prevd-or-backward-word + bind $argv \e\[3C nextd-or-forward-word + bind $argv \e\[3D prevd-or-backward-word + bind $argv \e\[1\;3C nextd-or-forward-word + bind $argv \e\[1\;3D prevd-or-backward-word - bind $argv \e\eOA history-token-search-backward - bind $argv \e\eOB history-token-search-forward - bind $argv \e\e\[A history-token-search-backward - bind $argv \e\e\[B history-token-search-forward - bind $argv \eO3A history-token-search-backward - bind $argv \eO3B history-token-search-forward - bind $argv \e\[3A history-token-search-backward - bind $argv \e\[3B history-token-search-forward - bind $argv \e\[1\;3A history-token-search-backward - bind $argv \e\[1\;3B history-token-search-forward + bind $argv \e\eOA history-token-search-backward + bind $argv \e\eOB history-token-search-forward + bind $argv \e\e\[A history-token-search-backward + bind $argv \e\e\[B history-token-search-forward + bind $argv \eO3A history-token-search-backward + bind $argv \eO3B history-token-search-forward + bind $argv \e\[3A history-token-search-backward + bind $argv \e\[3B history-token-search-forward + bind $argv \e\[1\;3A history-token-search-backward + bind $argv \e\[1\;3B history-token-search-forward - bind $argv \ca beginning-of-line - bind $argv \ce end-of-line - bind $argv \ey yank-pop - bind $argv \ch backward-delete-char - bind $argv \cp up-or-search - bind $argv \cn down-or-search - bind $argv \cf forward-char - bind $argv \cb backward-char - bind $argv \ct transpose-chars - bind $argv \et transpose-words - bind $argv \eu upcase-word + bind $argv \ca beginning-of-line + bind $argv \ce end-of-line + bind $argv \ey yank-pop + bind $argv \ch backward-delete-char + bind $argv \cp up-or-search + bind $argv \cn down-or-search + bind $argv \cf forward-char + bind $argv \cb backward-char + bind $argv \ct transpose-chars + bind $argv \et transpose-words + bind $argv \eu upcase-word - # This clashes with __fish_list_current_token - # bind $argv \el downcase-word - bind $argv \ec capitalize-word - bind $argv \e\x7f backward-kill-word - bind $argv \eb backward-word - bind $argv \ef forward-word - bind $argv \e\[1\;5C forward-word - bind $argv \e\[1\;5D backward-word - bind $argv \e\[1\;9A history-token-search-backward # iTerm2 - bind $argv \e\[1\;9B history-token-search-forward # iTerm2 - bind $argv \e\[1\;9C nextd-or-forward-word #iTerm2 - bind $argv \e\[1\;9D prevd-or-backward-word #iTerm2 - # Bash compatibility - # https://github.com/fish-shell/fish-shell/issues/89 - bind $argv \e. history-token-search-backward - bind $argv -k ppage beginning-of-history - bind $argv -k npage end-of-history - bind $argv \e\< beginning-of-buffer - bind $argv \e\> end-of-buffer + # This clashes with __fish_list_current_token + # bind $argv \el downcase-word + bind $argv \ec capitalize-word + bind $argv \e\x7f backward-kill-word + bind $argv \eb backward-word + bind $argv \ef forward-word + bind $argv \e\[1\;5C forward-word + bind $argv \e\[1\;5D backward-word + bind $argv \e\[1\;9A history-token-search-backward # iTerm2 + bind $argv \e\[1\;9B history-token-search-forward # iTerm2 + bind $argv \e\[1\;9C nextd-or-forward-word #iTerm2 + bind $argv \e\[1\;9D prevd-or-backward-word #iTerm2 + # Bash compatibility + # https://github.com/fish-shell/fish-shell/issues/89 + bind $argv \e. history-token-search-backward + bind $argv -k ppage beginning-of-history + bind $argv -k npage end-of-history + bind $argv \e\< beginning-of-buffer + bind $argv \e\> end-of-buffer - bind $argv \el __fish_list_current_token - bind $argv \ew 'set tok (commandline -pt); if test $tok[1]; echo; whatis $tok[1]; commandline -f repaint; end' - bind $argv \cl 'clear; commandline -f repaint' - bind $argv \cc __fish_cancel_commandline - bind $argv \cu backward-kill-line - bind $argv \cw backward-kill-path-component - bind $argv \ed 'set -l cmd (commandline); if test -z "$cmd"; echo; dirh; commandline -f repaint; else; commandline -f kill-word; end' - bind $argv \cd delete-or-exit + bind $argv \el __fish_list_current_token + bind $argv \ew 'set tok (commandline -pt); if test $tok[1]; echo; whatis $tok[1]; commandline -f repaint; end' + bind $argv \cl 'clear; commandline -f repaint' + bind $argv \cc __fish_cancel_commandline + bind $argv \cu backward-kill-line + bind $argv \cw backward-kill-path-component + bind $argv \ed 'set -l cmd (commandline); if test -z "$cmd"; echo; dirh; commandline -f repaint; else; commandline -f kill-word; end' + bind $argv \cd delete-or-exit - bind \ed forward-kill-word - bind \ed kill-word + bind \ed forward-kill-word + bind \ed kill-word - # Allow reading manpages by pressing F1 (many GUI applications) or Alt+h (like in zsh) - bind $argv -k f1 __fish_man_page - bind $argv \eh __fish_man_page + # Allow reading manpages by pressing F1 (many GUI applications) or Alt+h (like in zsh) + bind $argv -k f1 __fish_man_page + bind $argv \eh __fish_man_page - # This will make sure the output of the current command is paged using the default pager when you press Meta-p - # If none is set, less will be used - bind $argv \ep '__fish_paginate' - - # shift-tab does a tab complete followed by a search - bind $argv --key btab complete-and-search + # This will make sure the output of the current command is paged using the default pager when you press Meta-p + # If none is set, less will be used + bind $argv \ep '__fish_paginate' - # escape cancels stuff - bind \e cancel + # shift-tab does a tab complete followed by a search + bind $argv --key btab complete-and-search - # Ignore some known-bad control sequences - # https://github.com/fish-shell/fish-shell/issues/1917 - bind \e\[I 'begin;end' - bind \e\[O 'begin;end' + # escape cancels stuff + bind \e cancel - # term-specific special bindings - switch "$TERM" - case 'rxvt*' - bind $argv \e\[8~ end-of-line - bind $argv \eOc forward-word - bind $argv \eOd backward-word - end + # Ignore some known-bad control sequences + # https://github.com/fish-shell/fish-shell/issues/1917 + bind \e\[I 'begin;end' + bind \e\[O 'begin;end' - # Make it easy to turn an unexecuted command into a comment in the shell history. Also, - # remove the commenting chars so the command can be further edited then executed. - bind \e\# __fish_toggle_comment_commandline + # term-specific special bindings + switch "$TERM" + case 'rxvt*' + bind $argv \e\[8~ end-of-line + bind $argv \eOc forward-word + bind $argv \eOd backward-word + end + + # Make it easy to turn an unexecuted command into a comment in the shell history. Also, + # remove the commenting chars so the command can be further edited then executed. + bind \e\# __fish_toggle_comment_commandline end From 2418daebf36ad8bd00f43cb50f96f6d5531c49ae Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 25 May 2016 16:19:20 +0200 Subject: [PATCH 308/363] Add git hooks information and example This might be useful to contributors. --- CONTRIBUTING.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0052ddf7b..b8aabe283 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -160,6 +160,43 @@ You'll receive an email when the tests are complete telling you whether or not a You'll find the configuration used to control Travis in the `.travis.yml` file. +### Git hooks + +Since developers sometimes forget to run the tests, it can be helpful to use git hooks (see githooks(5)) to automate it. + +One possibility is a pre-push hook script like this one: + +```sh +#!/bin/sh +#### A pre-push hook for the fish-shell project +# This will run the tests when a push to master is detected, and will stop that if the tests fail +# Save this as .git/hooks/pre-push and make it executable + +protected_branch='master' + +# Git gives us lines like "refs/heads/frombranch SOMESHA1 refs/heads/tobranch SOMESHA1" +# We're only interested in the branches +while read from _ to _; do + if [ "x$to" = "xrefs/heads/$protected_branch" ]; then + isprotected=1 + fi +done +if [ "x$isprotected" = x1 ]; then + echo "Running tests before push to master" + make test + RESULT=$? + if [ $RESULT -ne 0 ]; then + echo "Tests failed for a push to master, we can't let you do that" >&2 + exit 1 + fi +fi +exit 0 +``` + +This will check if the push is to the master branch and, if it is, will run `make test` and only allow the push if that succeeds. In some circumstances it might be advisable to circumvent it with `git push --no-verify`, but usually that should not be necessary. + +To install the hook, put it in .git/hooks/pre-push and make it executable. + ## Installing the Required Tools ### Installing the Linting Tools From 2a8309458d52dbb1b4e4a07ad04b6d160ba88951 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 25 May 2016 16:26:07 +0200 Subject: [PATCH 309/363] Update changelog with clipboard change --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3189e8d5..d6a73ba92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# fish $nextversion (unreleased) + +## Significant changes + +- The clipboard integration has been removed in favor of explicit bindings (#3061) + # fish 2.3.0 (released May 20, 2016) There are no significant changes between 2.3.0 and 2.3b2. From 0d5ef3f43eecf1cd2f2e87ebe0b3c2b998f0efbd Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 25 May 2016 16:42:42 +0200 Subject: [PATCH 310/363] CONTRIBUTING.md: Describe emacs fish-mode setup --- CONTRIBUTING.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b8aabe283..9c93c7234 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -94,7 +94,12 @@ If you use Emacs: TBD If you use ViM: TBD -If you use Emacs: TBD +If you use Emacs: Install [fish-mode](https://github.com/wwwjfy/emacs-fish) (also available in melpa and melpa-stable) and `(setq-default indent-tabs-mode nil)` for it (via a hook or in `use-package`s ":init" block). It can also be made to run fish_indent via e.g. + +```elisp +(add-hook 'fish-mode-hook (lambda () + (add-hook 'before-save-hook 'fish_indent-before-save))) +``` ### Suppressing Reformatting of C++ Code From 23de5908cfd7f45eb879f28a6aa75187ed5a3262 Mon Sep 17 00:00:00 2001 From: David Adam Date: Wed, 25 May 2016 16:49:53 +0000 Subject: [PATCH 311/363] make_tarball: minor cleanup --- build_tools/make_tarball.sh | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/build_tools/make_tarball.sh b/build_tools/make_tarball.sh index 3e070c11f..5bbe9ebef 100755 --- a/build_tools/make_tarball.sh +++ b/build_tools/make_tarball.sh @@ -8,20 +8,19 @@ # Exit on error set -e -# We wil generate a tarball with a prefix "fish" +# We wil generate a tarball with a prefix "fish-VERSION" # git can do that automatically for us via git-archive -# but to get the documentation in, we need to make a symlink called "fish" +# but to get the documentation in, we need to make a symlink called "fish-VERSION" # and tar from that, so that the documentation gets the right prefix # Get the current directory, which we'll use for symlinks wd="$PWD" -# The name of the prefix, which is the directory that you get when you untar -prefix="fish" - # Get the version from git-describe VERSION=`git describe --dirty 2>/dev/null` -prefix="$prefix-$VERSION" + +# The name of the prefix, which is the directory that you get when you untar +prefix="fish-$VERSION" # The path where we will output the tar file path=~/fish_built/$prefix.tar From 79fa4d5c4a4ba152155d29c74208b6abef3f4d60 Mon Sep 17 00:00:00 2001 From: David Adam Date: Wed, 25 May 2016 16:51:04 +0000 Subject: [PATCH 312/363] make_tarball: allow output to an environment-controlled path --- build_tools/make_tarball.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build_tools/make_tarball.sh b/build_tools/make_tarball.sh index 5bbe9ebef..1231f5852 100755 --- a/build_tools/make_tarball.sh +++ b/build_tools/make_tarball.sh @@ -4,6 +4,7 @@ # We use git to output a tree. But we also want to build the user documentation # and put that in the tarball, so that nobody needs to have doxygen installed # to build it. +# Outputs to $FISH_ARTEFACT_PATH or ~/fish_built by default # Exit on error set -e @@ -23,7 +24,8 @@ VERSION=`git describe --dirty 2>/dev/null` prefix="fish-$VERSION" # The path where we will output the tar file -path=~/fish_built/$prefix.tar +# Defaults to ~/fish_built +path=${FISH_ARTEFACT_PATH:-~/fish_built}/$prefix.tar # Clean up stuff we've written before rm -f "$path" "$path".gz From b883e59ee97e0fb63e77adc4cf3dfb9ef77e0a10 Mon Sep 17 00:00:00 2001 From: David Adam Date: Wed, 25 May 2016 16:54:38 +0000 Subject: [PATCH 313/363] make_tarball: search for a tar that supports the options we need --- build_tools/make_tarball.sh | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/build_tools/make_tarball.sh b/build_tools/make_tarball.sh index 1231f5852..eec9d4360 100755 --- a/build_tools/make_tarball.sh +++ b/build_tools/make_tarball.sh @@ -14,6 +14,21 @@ set -e # but to get the documentation in, we need to make a symlink called "fish-VERSION" # and tar from that, so that the documentation gets the right prefix +# We need GNU tar as that supports the --mtime option +# BSD tar supports --mtree but keeping them in sync sounds too hard +TAR=notfound +for try in tar gtar gnutar; do + if $try -Pcf /dev/null --mtime now /dev/null >/dev/null 2>&1; then + TAR=$try + break + fi +done + +if [ "$TAR" = "notfound" ]; then + echo 'No suitable tar (supporting --mtime) found as tar/gtar/gnutar in PATH' + exit 1 +fi + # Get the current directory, which we'll use for symlinks wd="$PWD" @@ -43,7 +58,7 @@ echo $VERSION > version cd /tmp rm -f "$prefix" ln -s "$wd" "$prefix" -TAR_APPEND="gnutar --append --file=$path --mtime=now --owner=0 --group=0 --mode=g+w,a+rX" +TAR_APPEND="$TAR --append --file=$path --mtime=now --owner=0 --group=0 --mode=g+w,a+rX" $TAR_APPEND --no-recursion "$prefix"/user_doc $TAR_APPEND "$prefix"/user_doc/html "$prefix"/share/man $TAR_APPEND "$prefix"/version From 987f7cafd31d8366d8012a67e309a50bb914b32b Mon Sep 17 00:00:00 2001 From: David Adam Date: Wed, 25 May 2016 16:55:40 +0000 Subject: [PATCH 314/363] make_tarball: use a temporary directory rather than just /tmp --- build_tools/make_tarball.sh | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/build_tools/make_tarball.sh b/build_tools/make_tarball.sh index eec9d4360..c1c840e84 100755 --- a/build_tools/make_tarball.sh +++ b/build_tools/make_tarball.sh @@ -55,16 +55,21 @@ autoconf ./configure --with-doxygen make doc share/man echo $VERSION > version -cd /tmp -rm -f "$prefix" + +PREFIX_TMPDIR=`mktemp -d` +cd $PREFIX_TMPDIR + ln -s "$wd" "$prefix" TAR_APPEND="$TAR --append --file=$path --mtime=now --owner=0 --group=0 --mode=g+w,a+rX" $TAR_APPEND --no-recursion "$prefix"/user_doc $TAR_APPEND "$prefix"/user_doc/html "$prefix"/share/man $TAR_APPEND "$prefix"/version $TAR_APPEND "$prefix"/configure "$prefix"/config.h.in -rm -f "$prefix"/version -rm -f "$prefix" +rm "$prefix"/version +unlink "$prefix" + +cd - +rmdir $PREFIX_TMPDIR # gzip it gzip "$path" From 6d6f67ee67d35f19da21c48cfba194db107cebd5 Mon Sep 17 00:00:00 2001 From: David Adam Date: Wed, 25 May 2016 17:11:02 +0000 Subject: [PATCH 315/363] make_pkg: use a temporary directory rather than just /tmp --- build_tools/make_pkg.sh | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/build_tools/make_pkg.sh b/build_tools/make_pkg.sh index 7bc7c2eee..91d2e7f71 100755 --- a/build_tools/make_pkg.sh +++ b/build_tools/make_pkg.sh @@ -15,20 +15,23 @@ echo "Version is $VERSION" set -x make distclean -rm -Rf /tmp/fish_pkg #Exit on error set -e -mkdir -p /tmp/fish_pkg/root /tmp/fish_pkg/intermediates /tmp/fish_pkg/dst -xcodebuild install -scheme install_tree -configuration Release DSTROOT=/tmp/fish_pkg/root/ -pkgbuild --scripts build_tools/osx_package_scripts --root /tmp/fish_pkg/root/ --identifier 'com.ridiculousfish.fish-shell-pkg' --version "$VERSION" /tmp/fish_pkg/intermediates/fish.pkg +PKGDIR=`mktemp -d` -productbuild --package-path /tmp/fish_pkg/intermediates --distribution build_tools/osx_distribution.xml --resources build_tools/osx_package_resources/ ~/fish_built/fish-$VERSION.pkg +mkdir -p $PKGDIR/root $PKGDIR/intermediates $PKGDIR/dst +xcodebuild install -scheme install_tree -configuration Release DSTROOT=$PKGDIR/root/ +pkgbuild --scripts build_tools/osx_package_scripts --root $PKGDIR/root/ --identifier 'com.ridiculousfish.fish-shell-pkg' --version "$VERSION" $PKGDIR/intermediates/fish.pkg + +productbuild --package-path $PKGDIR/intermediates --distribution build_tools/osx_distribution.xml --resources build_tools/osx_package_resources/ ~/fish_built/fish-$VERSION.pkg # Make the app xcodebuild -scheme fish.app -configuration Release DSTROOT=/tmp/fish_app/ SYMROOT=DerivedData/fish/Build/Products -rm -f ~/fish_built/fish.app.zip + cd DerivedData/fish/Build/Products/Release/ zip -r ~/fish_built/fish-$VERSION.app.zip fish.app + +rm -r $PKGDIR From d7a4838a545beebad3786eb9289a04f87de480b6 Mon Sep 17 00:00:00 2001 From: David Adam Date: Wed, 25 May 2016 17:21:05 +0000 Subject: [PATCH 316/363] make_pkg: allow output to an environment-controlled path --- build_tools/make_pkg.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/build_tools/make_pkg.sh b/build_tools/make_pkg.sh index 91d2e7f71..84888321c 100755 --- a/build_tools/make_pkg.sh +++ b/build_tools/make_pkg.sh @@ -1,5 +1,7 @@ #!/bin/sh +# Script to produce an OS X installer .pkg and .app(.zip) + VERSION=`git describe --always --dirty 2>/dev/null` if test -z "$VERSION" ; then echo "Could not get version from git" @@ -21,17 +23,19 @@ set -e PKGDIR=`mktemp -d` +OUTPUT_PATH=${FISH_ARTEFACT_PATH:-~/fish_built} + mkdir -p $PKGDIR/root $PKGDIR/intermediates $PKGDIR/dst xcodebuild install -scheme install_tree -configuration Release DSTROOT=$PKGDIR/root/ pkgbuild --scripts build_tools/osx_package_scripts --root $PKGDIR/root/ --identifier 'com.ridiculousfish.fish-shell-pkg' --version "$VERSION" $PKGDIR/intermediates/fish.pkg -productbuild --package-path $PKGDIR/intermediates --distribution build_tools/osx_distribution.xml --resources build_tools/osx_package_resources/ ~/fish_built/fish-$VERSION.pkg +productbuild --package-path $PKGDIR/intermediates --distribution build_tools/osx_distribution.xml --resources build_tools/osx_package_resources/ $OUTPUT_PATH/fish-$VERSION.pkg # Make the app xcodebuild -scheme fish.app -configuration Release DSTROOT=/tmp/fish_app/ SYMROOT=DerivedData/fish/Build/Products cd DerivedData/fish/Build/Products/Release/ -zip -r ~/fish_built/fish-$VERSION.app.zip fish.app +zip -r $OUTPUT_PATH/fish-$VERSION.app.zip fish.app rm -r $PKGDIR From d1208386d21e4e85fa99538fef0bf23475a3c3e1 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Tue, 24 May 2016 20:42:50 -0700 Subject: [PATCH 317/363] tty driver ignore lnext (\cV) and werase (\cW) Configure the tty driver to ignore the lnext (\cV) and werase (\cW) characters so they can be bound to fish functions. Correct the `fish_key_bindings` program to initialize the tty in the same manner as the `fish` program. Fixes #3064 --- src/fish_key_reader.cpp | 52 +++++++++-------------------------------- src/reader.cpp | 23 +++++++----------- tests/bind.expect | 20 ++++++++++++++++ tests/bind.expect.out | 2 ++ 4 files changed, 42 insertions(+), 55 deletions(-) diff --git a/src/fish_key_reader.cpp b/src/fish_key_reader.cpp index 7908bfef7..1c30c1dbe 100644 --- a/src/fish_key_reader.cpp +++ b/src/fish_key_reader.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include @@ -25,15 +24,16 @@ #include "fallback.h" // IWYU pragma: keep #include "input.h" #include "input_common.h" +#include "proc.h" +#include "reader.h" struct config_paths_t determine_config_directory_paths(const char *argv0); -static struct termios saved_modes; // so we can reset the modes when we're done static long long int prev_tstamp = 0; static const char *ctrl_equivalents[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, "\\a", - "\\b", "\\t", "\\n", "\\v", "\\f", "\\r", NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, "\\e", NULL, NULL, NULL, NULL}; + "\\b", "\\t", "\\n", "\\v", "\\f", "\\r", NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, "\\e", NULL, NULL, NULL, NULL}; /// Return true if the recent sequence of characters indicates the user wants to exit the program. bool should_exit(unsigned char c) { @@ -47,7 +47,7 @@ bool should_exit(unsigned char c) { } /// Return the key name if the recent sequence of characters matches a known terminfo sequence. -char * const key_name(unsigned char c) { +char *const key_name(unsigned char c) { static char recent_chars[8] = {0}; recent_chars[0] = recent_chars[1]; @@ -121,7 +121,7 @@ void process_input(bool continuous_mode) { printf("%6lld usec dec: %3u hex: %2x char: %c\n", delta_tstamp, c, c, c); } - char * const name = key_name(c); + char *const name = key_name(c); if (name) { printf("FYI: Saw sequence for bind key name \"%s\"\n", name); free(name); @@ -136,48 +136,22 @@ void process_input(bool continuous_mode) { } } -/// Set the tty modes to not interpret any characters. We want every character to be passed thru to -/// this program. Including characters such as [ctrl-C] and [ctrl-D] that might normally have -/// special significance (e.g., terminate the program). -bool set_tty_modes(void) { - struct termios modes; - - tcgetattr(0, &modes); // get the current tty modes - saved_modes = modes; // save a copy so we can reset them on exit - - modes.c_lflag &= ~ICANON; // turn off canonical mode - modes.c_lflag &= ~ECHO; // turn off echo mode - modes.c_lflag &= ~ISIG; // turn off recognizing signal generating characters - modes.c_iflag &= ~ICRNL; // turn off mapping CR to NL - modes.c_iflag &= ~INLCR; // turn off mapping NL to CR - modes.c_cc[VMIN] = 1; // return each character as they arrive - modes.c_cc[VTIME] = 0; // wait forever for the next character - - if (tcsetattr(0, TCSANOW, &modes) != 0) { // set the new modes - return false; - } - return true; -} - -/// Restore the tty modes to what they were before this program was run. This shouldn't be required -/// but we do it just in case the program that ran us doesn't handle tty modes for external programs -/// in a sensible manner. -void reset_tty_modes() { tcsetattr(0, TCSANOW, &saved_modes); } - /// Make sure we cleanup before exiting if we're signaled. void signal_handler(int signo) { printf("\nExiting on receipt of signal #%d\n", signo); - reset_tty_modes(); + restore_term_mode(); exit(1); } /// Setup our environment (e.g., tty modes), process key strokes, then reset the environment. void setup_and_process_keys(bool continuous_mode) { + is_interactive_session = 1; // by definition this is interactive set_main_thread(); setup_fork_guards(); wsetlocale(LC_ALL, L"POSIX"); program_name = L"fish_key_reader"; env_init(); + reader_init(); input_init(); // Installing our handler for every signal (e.g., SIGSEGV) is dubious because it means that @@ -198,13 +172,9 @@ void setup_and_process_keys(bool continuous_mode) { set_wait_on_escape_ms(500); } - if (!set_tty_modes()) { - printf("Could not set the tty modes. Refusing to continue running.\n"); - exit(1); - } // TODO: We really should enable keypad mode but see issue #838. process_input(continuous_mode); - reset_tty_modes(); + restore_term_mode(); } int main(int argc, char **argv) { diff --git a/src/reader.cpp b/src/reader.cpp index 235086c9c..47258c64e 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -784,23 +784,19 @@ void reader_init() { // Set the mode used for the terminal, initialized to the current mode. memcpy(&shell_modes, &terminal_mode_on_startup, sizeof shell_modes); - shell_modes.c_iflag &= ~ICRNL; // turn off mapping CR (\cM) to NL (\cJ) - shell_modes.c_iflag &= ~INLCR; // turn off mapping NL (\cJ) to CR (\cM) + + shell_modes.c_iflag &= ~ICRNL; // disable mapping CR (\cM) to NL (\cJ) + shell_modes.c_iflag &= ~INLCR; // disable mapping NL (\cJ) to CR (\cM) + shell_modes.c_iflag &= ~IXON; // disable flow control + shell_modes.c_iflag &= ~IXOFF; // disable flow control + shell_modes.c_lflag &= ~ICANON; // turn off canonical mode shell_modes.c_lflag &= ~ECHO; // turn off echo mode - shell_modes.c_iflag &= ~IXON; // disable flow control - shell_modes.c_iflag &= ~IXOFF; // disable flow control + shell_modes.c_lflag &= ~IEXTEN; // turn off handling of discard and lnext characters + shell_modes.c_cc[VMIN] = 1; shell_modes.c_cc[VTIME] = 0; -#if defined(_POSIX_VDISABLE) -// 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 -#endif - // We don't use term_steal because this can fail if fd 0 isn't associated with a tty and this // function is run regardless of whether stdin is tied to a tty. This is harmless in that case. // We do it unconditionally because disabling ICRNL mode (see above) needs to be done at the @@ -1107,8 +1103,7 @@ wcstring completion_apply_to_command_line(const wcstring &val_str, complete_flag // Perform the insertion and compute the new location. wcstring result = command_line; result.insert(insertion_point, replaced); - size_t new_cursor_pos = - insertion_point + replaced.size() + (back_into_trailing_quote ? 1 : 0); + size_t new_cursor_pos = insertion_point + replaced.size() + (back_into_trailing_quote ? 1 : 0); if (add_space) { if (quote != L'\0' && unescaped_quote(command_line, insertion_point) != quote) { // This is a quoted parameter, first print a quote. diff --git a/tests/bind.expect b/tests/bind.expect index 7589fa8e8..7c5942410 100644 --- a/tests/bind.expect +++ b/tests/bind.expect @@ -199,3 +199,23 @@ expect_prompt -re {\r\nmno pqrt\r\n} { } unmatched { puts stderr "emacs transpose words fail, 100ms timeout: long delay" } + +# Verify special characters, such as \cV, are not intercepted by the kernel +# tty driver. Rather, they can be bound and handled by fish. +send "bind \\cV 'echo ctrl-v seen'\r" +expect_prompt +send "\026\r" +expect_prompt -re {ctrl-v seen} { + puts "ctrl-v seen" +} unmatched { + puts stderr "ctrl-v not seen" +} + +send "bind \\cO 'echo ctrl-o seen'\r" +expect_prompt +send "\017\r" +expect_prompt -re {ctrl-o seen} { + puts "ctrl-o seen" +} unmatched { + puts stderr "ctrl-o not seen" +} diff --git a/tests/bind.expect.out b/tests/bind.expect.out index f74d54b2f..07f09fdec 100644 --- a/tests/bind.expect.out +++ b/tests/bind.expect.out @@ -13,3 +13,5 @@ default-mode custom timeout set correctly emacs transpose words, 100ms timeout: no delay emacs transpose words, 100ms timeout: short delay emacs transpose words, 100ms timeout: long delay +ctrl-v seen +ctrl-o seen From 91962d8aa2924c35a8c221142bbf27e37d87efd9 Mon Sep 17 00:00:00 2001 From: David Adam Date: Wed, 25 May 2016 21:39:36 +0000 Subject: [PATCH 318/363] configure: move an errant space Introduced in 6b92fdd18d39. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index e457cd508..8b0ea8780 100644 --- a/configure.ac +++ b/configure.ac @@ -544,7 +544,7 @@ if test "x$included_pcre2" != "xyes"; then XLIBS="$LIBS" LIBS="$LIBS "`$PCRE2_CONFIG --libs$WCHAR_T_BITS 2>/dev/null` XCXXFLAGS="$CXXFLAGS" - CXXFLAGS="$CXXFLAGS" `$PCRE2_CONFIG --cflags` + CXXFLAGS="$CXXFLAGS "`$PCRE2_CONFIG --cflags` # cheat a bit here. the exact library is determined by $WCHAR_T_BITS, # and so AC_CHECK_LIB won't work (can't use a variable as library name) From 95635a5982ec8ab234d925a4bb5025328a706fa3 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 26 May 2016 16:11:26 +0200 Subject: [PATCH 319/363] Remove $version It's too generic a name - which both does not communicate what it is and prevents someone else from using it. --- src/env.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/env.cpp b/src/env.cpp index 433b45f90..381b45886 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -286,7 +286,7 @@ static bool variable_is_colon_delimited_array(const wcstring &str) { void env_init(const struct config_paths_t *paths /* or NULL */) { // env_read_only variables can not be altered directly by the user. const wchar_t *const ro_keys[] = { - L"status", L"history", L"version", L"_", L"LINES", L"COLUMNS", L"PWD", + L"status", L"history", L"_", L"LINES", L"COLUMNS", L"PWD", // L"SHLVL", // will be inserted a bit lower down L"FISH_VERSION", }; @@ -354,9 +354,8 @@ void env_init(const struct config_paths_t *paths /* or NULL */) { } } - // Set up the version variables. + // Set up the version variable. wcstring version = str2wcstring(get_fish_version()); - env_set(L"version", version.c_str(), ENV_GLOBAL); env_set(L"FISH_VERSION", version.c_str(), ENV_GLOBAL); // Set up SHLVL variable. From d55b226f191749282daba2c8c96315df993ecc3b Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 26 May 2016 16:22:53 +0200 Subject: [PATCH 320/363] Document the rest of the electric/ro vars Fixes #3072. --- doc_src/index.hdr.in | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index ee7fd8251..2136a9796 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -843,6 +843,14 @@ The user can change the settings of `fish` by changing the values of certain var - `CMD_DURATION`, the runtime of the last command in milliseconds. +- `FISH_VERSION`, the version of the currently running fish + +- `COLUMNS`, the current width of the terminal + +- `LINES`, the current height of the terminal + +- `SHLVL`, the level of nesting of shells + The names of these variables are mostly derived from the csh family of shells and differ from the ones used by Bourne style shells such as bash. Variables whose name are in uppercase are exported to the commands started by fish, while those in lowercase are not exported. This rule is not enforced by fish, but it is good coding practice to use casing to distinguish between exported and unexported variables. `fish` also uses several variables internally. Such variables are prefixed with the string `__FISH` or `__fish.` These should never be used by the user. Changing their value may break fish. From 8f420b9272e526097a73ad10ce74ce3edbb67b9e Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sun, 22 May 2016 19:00:13 -0700 Subject: [PATCH 321/363] Fix wide char related tests on Cygwin This makes the wide char tests run by `./fish_tests` pass on systems where sizeof wchar_t is two (e.g., Cygwin). In doing so it corrects several problems with the underlying code in module *utf8.cpp* such as allowing five and six byte UTF-8 sequences. They were allowed by the original Unicode proposal but are not allowed by the adopted standard. --- src/common.cpp | 31 +++++--- src/fish_tests.cpp | 190 +++++++++++++++++++-------------------------- src/utf8.cpp | 45 +++-------- src/utf8.h | 2 - 4 files changed, 113 insertions(+), 155 deletions(-) diff --git a/src/common.cpp b/src/common.cpp index 91b08824c..1ad51482a 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -106,8 +106,8 @@ demangled_backtrace(int max_frames, int skip_levels) { return backtrace_text; } -void __attribute__((noinline)) show_stackframe(const wchar_t msg_level, int frame_count, - int skip_levels) { +void __attribute__((noinline)) +show_stackframe(const wchar_t msg_level, int frame_count, int skip_levels) { ASSERT_IS_NOT_FORKED_CHILD(); // TODO: Decide if this is still needed. I'm commenting it out because it caused me some grief @@ -125,13 +125,13 @@ void __attribute__((noinline)) show_stackframe(const wchar_t msg_level, int fram } } -#else // HAVE_BACKTRACE_SYMBOLS +#else // HAVE_BACKTRACE_SYMBOLS -void __attribute__((noinline)) show_stackframe(const wchar_t msg_level, int frame_count, - int skip_levels) { +void __attribute__((noinline)) +show_stackframe(const wchar_t msg_level, int frame_count, int skip_levels) { debug_shared(msg_level, L"Sorry, but your system does not support backtraces"); } -#endif // HAVE_BACKTRACE_SYMBOLS +#endif // HAVE_BACKTRACE_SYMBOLS int fgetws2(wcstring *s, FILE *f) { int i = 0; @@ -179,8 +179,8 @@ static wcstring str2wcs_internal(const char *in, const size_t in_len) { result.reserve(in_len); size_t in_pos = 0; - if (MB_CUR_MAX == 1) // single-byte locale, all values are legal - { + if (MB_CUR_MAX == 1) { + // Single-byte locale, all values are legal. while (in_pos < in_len) { result.push_back((unsigned char)in[in_pos]); in_pos++; @@ -198,10 +198,16 @@ static wcstring str2wcs_internal(const char *in, const size_t in_len) { // Protect against broken mbrtowc() implementations which attempt to encode UTF-8 // sequences longer than four bytes (e.g., OS X Snow Leopard). use_encode_direct = true; + } else if (sizeof(wchar_t) == 2 && (in[in_pos] & 0xF8) == 0xF0) { + // Assume we are in a UTF-16 environment (e.g., Cygwin) using a UTF-8 encoding. + // The bits set check will be true for a four byte UTF-8 sequence that requires + // two UTF-16 chars. Something that doesn't work with our simple use of mbrtowc(). + use_encode_direct = true; } else { ret = mbrtowc(&wc, &in[in_pos], in_len - in_pos, &state); + // fprintf(stderr, "WTF in_pos %d ret %d\n", in_pos, ret); - // Determine whether to encode this characters with our crazy scheme. + // Determine whether to encode this character with our crazy scheme. if (wc >= ENCODE_DIRECT_BASE && wc < ENCODE_DIRECT_BASE + 256) { use_encode_direct = true; } else if (wc == INTERNAL_SEPARATOR) { @@ -215,20 +221,27 @@ static wcstring str2wcs_internal(const char *in, const size_t in_len) { } else if (ret > in_len - in_pos) { // Other error codes? Terrifying, should never happen. use_encode_direct = true; + } else if (sizeof(wchar_t) == 2 && wc >= 0xD800 && wc <= 0xDFFF) { + // If we get a surrogate pair char on a UTF-16 system (e.g., Cygwin) then + // it's guaranteed the UTF-8 decoding is wrong so use direct encoding. + use_encode_direct = true; } } if (use_encode_direct) { + // fprintf(stderr, "WTF use_encode_direct\n"); wc = ENCODE_DIRECT_BASE + (unsigned char)in[in_pos]; result.push_back(wc); in_pos++; memset(&state, 0, sizeof state); } else if (ret == 0) { + // fprintf(stderr, "WTF null byte\n"); // Embedded null byte! result.push_back(L'\0'); in_pos++; memset(&state, 0, sizeof state); } else { + // fprintf(stderr, "WTF null byte\n"); // Normal case. result.push_back(wc); in_pos += ret; diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 2457cee49..49c9327bb 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -17,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -68,6 +68,8 @@ static const char *const *s_arguments; static int s_test_run_count = 0; +bool is_wchar_ucs2() { return sizeof(wchar_t) == 2; } + // Indicate if we should test the given function. Either we test everything (all arguments) or we // run only tests that have a prefix in s_arguments. static bool should_test_function(const char *func_name) { @@ -194,8 +196,12 @@ static void test_unescape_sane() { if (unescape_string(L"echo \\U110000", &output, UNESCAPE_DEFAULT)) { err(L"Should not have been able to unescape \\U110000\n"); } - if (!unescape_string(L"echo \\U10FFFF", &output, UNESCAPE_DEFAULT)) { - err(L"Should have been able to unescape \\U10FFFF\n"); + if (is_wchar_ucs2()) { + // TODO: Make this work on MS Windows. + } else { + if (!unescape_string(L"echo \\U10FFFF", &output, UNESCAPE_DEFAULT)) { + err(L"Should have been able to unescape \\U10FFFF\n"); + } } } @@ -271,36 +277,8 @@ static char *str2hex(const char *input) { } /// Test wide/narrow conversion by creating random strings and verifying that the original string -/// comes back thorugh double conversion. +/// comes back through double conversion. static void test_convert() { -#if 0 - char o[] = {-17, -128, -121, -68, 0}; - - wchar_t *w = str2wcs(o); - char *n = wcs2str(w); - int i; - - for (i = 0; o[i]; i++) { - bitprint(o[i]); - // wprintf(L"%d ", o[i]); - } - wprintf(L"\n"); - - for (i = 0; w[i]; i++) { - wbitprint(w[i]); - // wprintf(L"%d ", w[i]); - } - wprintf(L"\n"); - - for (i = 0; n[i]; i++) { - bitprint(n[i]); - // wprintf(L"%d ", n[i]); - } - wprintf(L"\n"); - - return; -#endif - int i; std::vector sb; @@ -308,11 +286,9 @@ static void test_convert() { for (i = 0; i < ESCAPE_TEST_COUNT; i++) { const char *o, *n; - char c; sb.clear(); - while (rand() % ESCAPE_TEST_LENGTH) { c = rand(); sb.push_back(c); @@ -344,7 +320,7 @@ static void test_convert() { /// Verify correct behavior with embedded nulls. static void test_convert_nulls(void) { - say(L"Testing embedded nulls in string conversion"); + say(L"Testing convert_nulls"); const wchar_t in[] = L"AAA\0BBB"; const size_t in_len = (sizeof in / sizeof *in) - 1; const wcstring in_str = wcstring(in, in_len); @@ -370,9 +346,9 @@ static void test_convert_nulls(void) { } /// Test the tokenizer. -static void test_tok() { - tok_t token; +static void test_tokenizer() { say(L"Testing tokenizer"); + tok_t token; const wchar_t *str = L"string &1 'nested \"quoted\" '(string containing subshells " @@ -515,7 +491,6 @@ static parser_test_error_bits_t detect_argument_errors(const wcstring &src) { /// Test the parser. static void test_parser() { say(L"Testing parser"); - parser_t parser; say(L"Testing block nesting"); @@ -961,88 +936,55 @@ static void test_wchar2utf8(const wchar_t *src, size_t slen, const unsigned char } static void test_utf8() { + say(L"Testing utf8"); wchar_t w1[] = {0x54, 0x65, 0x73, 0x74}; wchar_t w2[] = {0x0422, 0x0435, 0x0441, 0x0442}; wchar_t w3[] = {0x800, 0x1e80, 0x98c4, 0x9910, 0xff00}; - wchar_t w4[] = {0x15555, 0xf7777, 0xa}; - wchar_t w5[] = {0x255555, 0x1fa04ff, 0xddfd04, 0xa}; - wchar_t w6[] = {0xf255555, 0x1dfa04ff, 0x7fddfd04, 0xa}; + wchar_t w4[] = {0x15555, 0xf7777, 0x0a}; wchar_t wb[] = {(wchar_t)-2, 0xa, (wchar_t)0xffffffff, 0x0441}; - wchar_t wm[] = {0x41, 0x0441, 0x3042, 0xff67, 0x9b0d, 0x2e05da67}; - wchar_t wb1[] = {0xa, 0x0422}; - wchar_t wb2[] = {0xd800, 0xda00, 0x41, 0xdfff, 0xa}; - wchar_t wbom[] = {0xfeff, 0x41, 0xa}; + wchar_t wm[] = {0x41, 0x0441, 0x3042, 0xff67, 0x9b0d}; + wchar_t wb1[] = {0x0a, 0x0422}; + wchar_t wb2[] = {0xd800, 0xda00, 0x41, 0xdfff, 0x0a}; + wchar_t wbom[] = {0xfeff, 0x41, 0x0a}; wchar_t wbom2[] = {0x41, 0xa}; - wchar_t wbom22[] = {0xfeff, 0x41, 0xa}; + wchar_t wbom22[] = {0xfeff, 0x41, 0x0a}; unsigned char u1[] = {0x54, 0x65, 0x73, 0x74}; unsigned char u2[] = {0xd0, 0xa2, 0xd0, 0xb5, 0xd1, 0x81, 0xd1, 0x82}; unsigned char u3[] = {0xe0, 0xa0, 0x80, 0xe1, 0xba, 0x80, 0xe9, 0xa3, 0x84, 0xe9, 0xa4, 0x90, 0xef, 0xbc, 0x80}; - unsigned char u4[] = {0xf0, 0x95, 0x95, 0x95, 0xf3, 0xb7, 0x9d, 0xb7, 0xa}; - unsigned char u5[] = {0xf8, 0x89, 0x95, 0x95, 0x95, 0xf9, 0xbe, 0xa0, - 0x93, 0xbf, 0xf8, 0xb7, 0x9f, 0xb4, 0x84, 0x0a}; - unsigned char u6[] = {0xfc, 0x8f, 0x89, 0x95, 0x95, 0x95, 0xfc, 0x9d, 0xbe, 0xa0, - 0x93, 0xbf, 0xfd, 0xbf, 0xb7, 0x9f, 0xb4, 0x84, 0x0a}; + unsigned char u4[] = {0xf0, 0x95, 0x95, 0x95, 0xf3, 0xb7, 0x9d, 0xb7, 0x0a}; unsigned char ub[] = {0xa, 0xd1, 0x81}; - unsigned char um[] = {0x41, 0xd1, 0x81, 0xe3, 0x81, 0x82, 0xef, 0xbd, 0xa7, - 0xe9, 0xac, 0x8d, 0xfc, 0xae, 0x81, 0x9d, 0xa9, 0xa7}; + unsigned char um[] = {0x41, 0xd1, 0x81, 0xe3, 0x81, 0x82, 0xef, 0xbd, 0xa7, 0xe9, 0xac, 0x8d}; unsigned char ub1[] = {0xa, 0xff, 0xd0, 0xa2, 0xfe, 0x8f, 0xe0, 0x80}; unsigned char uc080[] = {0xc0, 0x80}; - unsigned char ub2[] = {0xed, 0xa1, 0x8c, 0xed, 0xbe, 0xb4, 0xa}; + unsigned char ub2[] = {0xed, 0xa1, 0x8c, 0xed, 0xbe, 0xb4, 0x0a}; unsigned char ubom[] = {0x41, 0xa}; - unsigned char ubom2[] = {0xef, 0xbb, 0xbf, 0x41, 0xa}; + unsigned char ubom2[] = {0xef, 0xbb, 0xbf, 0x41, 0x0a}; // UTF-8 -> UCS-4 string. test_utf82wchar(ubom2, sizeof(ubom2), wbom2, sizeof(wbom2) / sizeof(*wbom2), UTF8_SKIP_BOM, - sizeof(wbom2) / sizeof(*wbom2), "skip BOM"); + sizeof(wbom2) / sizeof(*wbom2), "ubom2 skip BOM"); test_utf82wchar(ubom2, sizeof(ubom2), wbom22, sizeof(wbom22) / sizeof(*wbom22), 0, - sizeof(wbom22) / sizeof(*wbom22), "BOM"); - test_utf82wchar(uc080, sizeof(uc080), NULL, 0, 0, 0, "c0 80 - forbitten by rfc3629"); - test_utf82wchar(ub2, sizeof(ub2), NULL, 0, 0, is_wchar_ucs2() ? 0 : 3, - "resulted in forbitten wchars (len)"); + sizeof(wbom22) / sizeof(*wbom22), "ubom2 BOM"); + test_utf82wchar(uc080, sizeof(uc080), NULL, 0, 0, 0, "uc080 c0 80 - forbitten by rfc3629"); + test_utf82wchar(ub2, sizeof(ub2), NULL, 0, 0, 3, "ub2 resulted in forbitten wchars (len)"); test_utf82wchar(ub2, sizeof(ub2), wb2, sizeof(wb2) / sizeof(*wb2), 0, 0, - "resulted in forbitten wchars"); + "ub2 resulted in forbitten wchars"); test_utf82wchar(ub2, sizeof(ub2), L"\x0a", 1, UTF8_IGNORE_ERROR, 1, - "resulted in ignored forbitten wchars"); + "ub2 resulted in ignored forbitten wchars"); test_utf82wchar(u1, sizeof(u1), w1, sizeof(w1) / sizeof(*w1), 0, sizeof(w1) / sizeof(*w1), - "1 octet chars"); + "u1/w1 1 octet chars"); test_utf82wchar(u2, sizeof(u2), w2, sizeof(w2) / sizeof(*w2), 0, sizeof(w2) / sizeof(*w2), - "2 octets chars"); + "u2/w2 2 octets chars"); test_utf82wchar(u3, sizeof(u3), w3, sizeof(w3) / sizeof(*w3), 0, sizeof(w3) / sizeof(*w3), - "3 octets chars"); - test_utf82wchar(u4, sizeof(u4), w4, sizeof(w4) / sizeof(*w4), 0, sizeof(w4) / sizeof(*w4), - "4 octets chars"); - test_utf82wchar(u5, sizeof(u5), w5, sizeof(w5) / sizeof(*w5), 0, sizeof(w5) / sizeof(*w5), - "5 octets chars"); - test_utf82wchar(u6, sizeof(u6), w6, sizeof(w6) / sizeof(*w6), 0, sizeof(w6) / sizeof(*w6), - "6 octets chars"); + "u3/w3 3 octets chars"); test_utf82wchar("\xff", 1, NULL, 0, 0, 0, "broken utf-8 0xff symbol"); test_utf82wchar("\xfe", 1, NULL, 0, 0, 0, "broken utf-8 0xfe symbol"); test_utf82wchar("\x8f", 1, NULL, 0, 0, 0, "broken utf-8, start from 10 higher bits"); - if (!is_wchar_ucs2()) - test_utf82wchar(ub1, sizeof(ub1), wb1, sizeof(wb1) / sizeof(*wb1), UTF8_IGNORE_ERROR, - sizeof(wb1) / sizeof(*wb1), "ignore bad chars"); - test_utf82wchar(um, sizeof(um), wm, sizeof(wm) / sizeof(*wm), 0, sizeof(wm) / sizeof(*wm), - "mixed languages"); - // PCA this test was to ensure that if the output buffer was too small, we'd get 0 - // we no longer have statically sized result buffers, so this test is disabled - // test_utf82wchar(um, sizeof(um), wm, sizeof(wm) / sizeof(*wm) - 1, 0, - // 0, "boundaries -1"); - test_utf82wchar(um, sizeof(um), wm, sizeof(wm) / sizeof(*wm) + 1, 0, sizeof(wm) / sizeof(*wm), - "boundaries +1"); - test_utf82wchar(um, sizeof(um), NULL, 0, 0, sizeof(wm) / sizeof(*wm), "calculate length"); - test_utf82wchar(ub1, sizeof(ub1), NULL, 0, 0, 0, "calculate length of bad chars"); - test_utf82wchar(ub1, sizeof(ub1), NULL, 0, UTF8_IGNORE_ERROR, sizeof(wb1) / sizeof(*wb1), - "calculate length, ignore bad chars"); test_utf82wchar((const char *)NULL, 0, NULL, 0, 0, 0, "invalid params, all 0"); test_utf82wchar(u1, 0, NULL, 0, 0, 0, "invalid params, src buf not NULL"); test_utf82wchar((const char *)NULL, 10, NULL, 0, 0, 0, "invalid params, src length is not 0"); - // PCA this test was to ensure that converting into a zero length output buffer would return 0 - // we no longer statically size output buffers, so the test is disabled - // test_utf82wchar(u1, sizeof(u1), w1, 0, 0, 0, - // "invalid params, dst is not NULL"); - // UCS-4 -> UTF-8 string. const char *const nullc = NULL; test_wchar2utf8(wbom, sizeof(wbom) / sizeof(*wbom), ubom, sizeof(ubom), UTF8_SKIP_BOM, @@ -1050,31 +992,54 @@ static void test_utf8() { test_wchar2utf8(wb2, sizeof(wb2) / sizeof(*wb2), nullc, 0, 0, 0, "prohibited wchars"); test_wchar2utf8(wb2, sizeof(wb2) / sizeof(*wb2), nullc, 0, UTF8_IGNORE_ERROR, 2, "ignore prohibited wchars"); - test_wchar2utf8(w1, sizeof(w1) / sizeof(*w1), u1, sizeof(u1), 0, sizeof(u1), "1 octet chars"); - test_wchar2utf8(w2, sizeof(w2) / sizeof(*w2), u2, sizeof(u2), 0, sizeof(u2), "2 octets chars"); - test_wchar2utf8(w3, sizeof(w3) / sizeof(*w3), u3, sizeof(u3), 0, sizeof(u3), "3 octets chars"); - test_wchar2utf8(w4, sizeof(w4) / sizeof(*w4), u4, sizeof(u4), 0, sizeof(u4), "4 octets chars"); - test_wchar2utf8(w5, sizeof(w5) / sizeof(*w5), u5, sizeof(u5), 0, sizeof(u5), "5 octets chars"); - test_wchar2utf8(w6, sizeof(w6) / sizeof(*w6), u6, sizeof(u6), 0, sizeof(u6), "6 octets chars"); - test_wchar2utf8(wb, sizeof(wb) / sizeof(*wb), ub, sizeof(ub), 0, 0, "bad chars"); - test_wchar2utf8(wb, sizeof(wb) / sizeof(*wb), ub, sizeof(ub), UTF8_IGNORE_ERROR, sizeof(ub), - "ignore bad chars"); - test_wchar2utf8(wm, sizeof(wm) / sizeof(*wm), um, sizeof(um), 0, sizeof(um), "mixed languages"); - test_wchar2utf8(wm, sizeof(wm) / sizeof(*wm), um, sizeof(um) - 1, 0, 0, "boundaries -1"); - test_wchar2utf8(wm, sizeof(wm) / sizeof(*wm), um, sizeof(um) + 1, 0, sizeof(um), - "boundaries +1"); - test_wchar2utf8(wm, sizeof(wm) / sizeof(*wm), nullc, 0, 0, sizeof(um), "calculate length"); - test_wchar2utf8(wb, sizeof(wb) / sizeof(*wb), nullc, 0, 0, 0, "calculate length of bad chars"); - test_wchar2utf8(wb, sizeof(wb) / sizeof(*wb), nullc, 0, UTF8_IGNORE_ERROR, sizeof(ub), - "calculate length, ignore bad chars"); + test_wchar2utf8(w1, sizeof(w1) / sizeof(*w1), u1, sizeof(u1), 0, sizeof(u1), + "w1/u1 1 octet chars"); + test_wchar2utf8(w2, sizeof(w2) / sizeof(*w2), u2, sizeof(u2), 0, sizeof(u2), + "w2/u2 2 octets chars"); + test_wchar2utf8(w3, sizeof(w3) / sizeof(*w3), u3, sizeof(u3), 0, sizeof(u3), + "w3/u3 3 octets chars"); test_wchar2utf8(NULL, 0, nullc, 0, 0, 0, "invalid params, all 0"); test_wchar2utf8(w1, 0, nullc, 0, 0, 0, "invalid params, src buf not NULL"); - test_wchar2utf8(NULL, 10, nullc, 0, 0, 0, "invalid params, src length is not 0"); test_wchar2utf8(w1, sizeof(w1) / sizeof(*w1), u1, 0, 0, 0, "invalid params, dst is not NULL"); + test_wchar2utf8(NULL, 10, nullc, 0, 0, 0, "invalid params, src length is not 0"); + + // The following tests won't pass on systems (e.g., Cygwin) where sizeof wchar_t is 2. That's + // due to several reasons but the primary one is that narrowing conversions of literals assigned + // to the wchar_t arrays above don't result in values that will be treated as errors by the + // conversion functions. + if (is_wchar_ucs2()) return; + test_utf82wchar(u4, sizeof(u4), w4, sizeof(w4) / sizeof(*w4), 0, sizeof(w4) / sizeof(*w4), + "u4/w4 4 octets chars"); + test_wchar2utf8(w4, sizeof(w4) / sizeof(*w4), u4, sizeof(u4), 0, sizeof(u4), + "w4/u4 4 octets chars"); + test_wchar2utf8(wb, sizeof(wb) / sizeof(*wb), ub, sizeof(ub), 0, 0, "wb/ub bad chars"); + test_wchar2utf8(wb, sizeof(wb) / sizeof(*wb), ub, sizeof(ub), UTF8_IGNORE_ERROR, sizeof(ub), + "wb/ub ignore bad chars"); + test_wchar2utf8(wm, sizeof(wm) / sizeof(*wm), um, sizeof(um), 0, sizeof(um), + "wm/um mixed languages"); + test_wchar2utf8(wm, sizeof(wm) / sizeof(*wm), um, sizeof(um) - 1, 0, 0, "wm/um boundaries -1"); + test_wchar2utf8(wm, sizeof(wm) / sizeof(*wm), um, sizeof(um) + 1, 0, sizeof(um), + "wm/um boundaries +1"); + test_wchar2utf8(wm, sizeof(wm) / sizeof(*wm), nullc, 0, 0, sizeof(um), + "wm/um calculate length"); + test_wchar2utf8(wb, sizeof(wb) / sizeof(*wb), nullc, 0, 0, 0, + "wb calculate length of bad chars"); + test_wchar2utf8(wb, sizeof(wb) / sizeof(*wb), nullc, 0, UTF8_IGNORE_ERROR, sizeof(ub), + "calculate length, ignore bad chars"); + test_utf82wchar(ub1, sizeof(ub1), wb1, sizeof(wb1) / sizeof(*wb1), UTF8_IGNORE_ERROR, + sizeof(wb1) / sizeof(*wb1), "ub1/wb1 ignore bad chars"); + test_utf82wchar(um, sizeof(um), wm, sizeof(wm) / sizeof(*wm), 0, sizeof(wm) / sizeof(*wm), + "um/wm mixed languages"); + test_utf82wchar(um, sizeof(um), wm, sizeof(wm) / sizeof(*wm) + 1, 0, sizeof(wm) / sizeof(*wm), + "um/wm boundaries +1"); + test_utf82wchar(um, sizeof(um), NULL, 0, 0, sizeof(wm) / sizeof(*wm), "um/wm calculate length"); + test_utf82wchar(ub1, sizeof(ub1), NULL, 0, 0, 0, "ub1 calculate length of bad chars"); + test_utf82wchar(ub1, sizeof(ub1), NULL, 0, UTF8_IGNORE_ERROR, sizeof(wb1) / sizeof(*wb1), + "ub1 calculate length, ignore bad chars"); } static void test_escape_sequences(void) { - say(L"Testing escape codes"); + say(L"Testing escape_sequences"); if (escape_code_length(L"") != 0) err(L"test_escape_sequences failed on line %d\n", __LINE__); if (escape_code_length(L"abcd") != 0) err(L"test_escape_sequences failed on line %d\n", __LINE__); @@ -3905,6 +3870,9 @@ int main(int argc, char **argv) { program_name = L"(ignore)"; s_arguments = argv + 1; + struct utsname uname_info; + uname(&uname_info); + say(L"Testing low-level functionality"); set_main_thread(); setup_fork_guards(); @@ -3931,7 +3899,7 @@ int main(int argc, char **argv) { if (should_test_function("format")) test_format(); if (should_test_function("convert")) test_convert(); if (should_test_function("convert_nulls")) test_convert_nulls(); - if (should_test_function("tok")) test_tok(); + if (should_test_function("tok")) test_tokenizer(); if (should_test_function("iothread")) test_iothread(); if (should_test_function("parser")) test_parser(); if (should_test_function("cancellation")) test_cancellation(); diff --git a/src/utf8.cpp b/src/utf8.cpp index e5a4178a4..8c894800d 100644 --- a/src/utf8.cpp +++ b/src/utf8.cpp @@ -26,8 +26,6 @@ #define _SEQ2 0xc0 #define _SEQ3 0xe0 #define _SEQ4 0xf0 -#define _SEQ5 0xf8 -#define _SEQ6 0xfc #define _BOM 0xfeff @@ -37,8 +35,6 @@ typedef wchar_t utf8_wchar_t; typedef std::basic_string utf8_wstring_t; -bool is_wchar_ucs2() { return UTF8_WCHAR_MAX <= 0xFFFF; } - static size_t utf8_to_wchar_internal(const char *in, size_t insize, utf8_wstring_t *result, int flags); static size_t wchar_to_utf8_internal(const utf8_wchar_t *in, size_t insize, char *out, @@ -195,12 +191,6 @@ static size_t utf8_to_wchar_internal(const char *in, size_t insize, utf8_wstring } else if ((*p & 0xf8) == _SEQ4) { n = 4; high = (utf8_wchar_t)(*p & 0x07); - } else if ((*p & 0xfc) == _SEQ5) { - n = 5; - high = (utf8_wchar_t)(*p & 0x03); - } else if ((*p & 0xfe) == _SEQ6) { - n = 6; - high = (utf8_wchar_t)(*p & 0x01); } else { if ((flags & UTF8_IGNORE_ERROR) == 0) return 0; continue; @@ -298,12 +288,18 @@ static size_t wchar_to_utf8_internal(const utf8_wchar_t *in, size_t insize, char if ((flags & UTF8_IGNORE_ERROR) == 0) return 0; continue; } - if (w_wide <= 0x0000007f) n = 1; - else if (w_wide <= 0x000007ff) n = 2; - else if (w_wide <= 0x0000ffff) n = 3; - else if (w_wide <= 0x001fffff) n = 4; - else if (w_wide <= 0x03ffffff) n = 5; - else n = 6; /// if (w_wide <= 0x7fffffff) + if (w_wide <= 0x0000007f) + n = 1; + else if (w_wide <= 0x000007ff) + n = 2; + else if (w_wide <= 0x0000ffff) + n = 3; + else if (w_wide <= 0x001fffff) + n = 4; + else if (w_wide <= 0x03ffffff) + n = 5; + else + n = 6; /// if (w_wide <= 0x7fffffff) total += n; @@ -345,23 +341,6 @@ static size_t wchar_to_utf8_internal(const utf8_wchar_t *in, size_t insize, char p[0] = _SEQ4 | ((oc[1] & 0x1f) >> 2); break; } - case 5: { - p[4] = _NXT | (oc[3] & 0x3f); - p[3] = _NXT | (oc[3] >> 6) | ((oc[2] & 0x0f) << 2); - p[2] = _NXT | ((oc[2] & 0xf0) >> 4) | ((oc[1] & 0x03) << 4); - p[1] = _NXT | (oc[1] >> 2); - p[0] = _SEQ5 | (oc[0] & 0x03); - break; - } - case 6: { - p[5] = _NXT | (oc[3] & 0x3f); - p[4] = _NXT | (oc[3] >> 6) | ((oc[2] & 0x0f) << 2); - p[3] = _NXT | (oc[2] >> 4) | ((oc[1] & 0x03) << 4); - p[2] = _NXT | (oc[1] >> 2); - p[1] = _NXT | (oc[0] & 0x3f); - p[0] = _SEQ6 | ((oc[0] & 0x40) >> 6); - break; - } } // NOTE: do not check here for forbitten UTF-8 characters. They cannot appear here because diff --git a/src/utf8.h b/src/utf8.h index 99a12a8e6..3879459b4 100644 --- a/src/utf8.h +++ b/src/utf8.h @@ -33,6 +33,4 @@ bool wchar_to_utf8_string(const std::wstring &input, std::string *result); size_t utf8_to_wchar(const char *in, size_t insize, std::wstring *out, int flags); size_t wchar_to_utf8(const wchar_t *in, size_t insize, char *out, size_t outsize, int flags); -bool is_wchar_ucs2(); - #endif /* !_UTF8_H_ */ From 9ad3488b5d076729b39bd3f91abda4fd10bea21c Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Fri, 27 May 2016 14:41:16 -0700 Subject: [PATCH 322/363] fix some style bogosities that crept in --- src/complete.cpp | 2 +- src/expand.cpp | 6 ++---- src/fallback.cpp | 8 +++++--- src/history.cpp | 11 ++++++----- src/parse_util.cpp | 4 ++-- src/parser_keywords.cpp | 2 +- src/proc.cpp | 4 +--- src/screen.cpp | 4 ++-- src/wcstringutil.cpp | 2 +- src/wgetopt.cpp | 4 ++-- src/wildcard.cpp | 4 +--- src/wutil.cpp | 6 +++--- 12 files changed, 27 insertions(+), 30 deletions(-) diff --git a/src/complete.cpp b/src/complete.cpp index 8c38b2663..59da5a94e 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -1340,7 +1340,7 @@ void complete(const wcstring &cmd_with_subcmds, std::vector *out_c } } - if (cmd_node && cmd_node->location_in_or_at_end_of_source_range(pos)) { + if (cmd_node && cmd_node->location_in_or_at_end_of_source_range(pos)) { // Complete command filename. completer.complete_cmd(current_token, use_function, use_builtin, use_command, use_implicit_cd); diff --git a/src/expand.cpp b/src/expand.cpp index f8cd75508..b69c79f82 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -917,8 +917,7 @@ static int expand_variables(const wcstring &instr, std::vector *ou wchar_t *slice_end; size_t bad_pos; - bad_pos = - parse_slice(in + slice_start, &slice_end, var_idx_list, var_pos_list, 1); + bad_pos = parse_slice(in + slice_start, &slice_end, var_idx_list, var_pos_list, 1); if (bad_pos != 0) { append_syntax_error(errors, stop_pos + bad_pos, L"Invalid index value"); is_ok = 0; @@ -931,8 +930,7 @@ static int expand_variables(const wcstring &instr, std::vector *ou long tmp = var_idx_list.at(j); if (tmp != 1) { size_t var_src_pos = var_pos_list.at(j); - append_syntax_error(errors, slice_start + var_src_pos, - ARRAY_BOUNDS_ERR); + append_syntax_error(errors, slice_start + var_src_pos, ARRAY_BOUNDS_ERR); is_ok = 0; return is_ok; } diff --git a/src/fallback.cpp b/src/fallback.cpp index dc5bcce2f..b14b92434 100644 --- a/src/fallback.cpp +++ b/src/fallback.cpp @@ -148,11 +148,13 @@ int wcsncasecmp_use_weak(const wchar_t *s1, const wchar_t *s2, size_t n) { if (&wcsncasecmp != NULL) return (wcsncasecmp)(s1, s2, n); return wcsncasecmp_fallback(s1, s2, n); } -#else // __DARWIN_C_LEVEL >= 200809L +#else // __DARWIN_C_LEVEL >= 200809L wchar_t *wcsdup(const wchar_t *in) { return wcsdup_fallback(in); } int wcscasecmp(const wchar_t *a, const wchar_t *b) { return wcscasecmp_fallback(a, b); } -int wcsncasecmp(const wchar_t *a, const wchar_t *b, size_t n) { return wcsncasecmp_fallback(a, b, n); } -#endif // __DARWIN_C_LEVEL >= 200809L +int wcsncasecmp(const wchar_t *a, const wchar_t *b, size_t n) { + return wcsncasecmp_fallback(a, b, n); +} +#endif // __DARWIN_C_LEVEL >= 200809L #endif // __APPLE__ #ifndef HAVE_WCSNDUP diff --git a/src/history.cpp b/src/history.cpp index ca61ed3ce..e849fb872 100644 --- a/src/history.cpp +++ b/src/history.cpp @@ -669,8 +669,7 @@ static size_t offset_of_next_item(const char *begin, size_t mmap_length, break; } case history_type_fish_1_x: { - result = - offset_of_next_item_fish_1_x(begin, mmap_length, inout_cursor); + result = offset_of_next_item_fish_1_x(begin, mmap_length, inout_cursor); break; } case history_type_unknown: { @@ -755,7 +754,8 @@ void history_t::save_internal_unless_disabled() { } // This might be a good candidate for moving to a background thread. - time_profiler_t profiler(vacuum ? "save_internal vacuum" : "save_internal no vacuum"); //!OCLINT(side-effect) + time_profiler_t profiler(vacuum ? "save_internal vacuum" + : "save_internal no vacuum"); //!OCLINT(side-effect) this->save_internal(vacuum); // Update our countdown. @@ -1414,7 +1414,7 @@ void history_t::disable_automatic_saving() { } void history_t::enable_automatic_saving() { - scoped_lock locker(lock); //!OCLINT(side-effect) + scoped_lock locker(lock); //!OCLINT(side-effect) assert(disable_automatic_save_counter > 0); // underflow disable_automatic_save_counter--; save_internal_unless_disabled(); @@ -1614,7 +1614,8 @@ static int threaded_perform_file_detection(file_detection_context_t *ctx) { return ctx->perform_file_detection(true /* test all */); } -static void perform_file_detection_done(file_detection_context_t *ctx, int success) { //!OCLINT(success is ignored) +static void perform_file_detection_done(file_detection_context_t *ctx, + int success) { //!OCLINT(success is ignored) ASSERT_IS_MAIN_THREAD(); // Now that file detection is done, update the history item with the valid file paths. diff --git a/src/parse_util.cpp b/src/parse_util.cpp index b78e4a8e4..48ff69816 100644 --- a/src/parse_util.cpp +++ b/src/parse_util.cpp @@ -1054,12 +1054,12 @@ parser_test_error_bits_t parse_util_detect_errors_in_argument(const parse_node_t // We have something like $$$^.... Back up until we reach the first $. size_t first_dollar = idx; while (first_dollar > 0 && - (unesc.at(first_dollar - 1) == VARIABLE_EXPAND || + (unesc.at(first_dollar - 1) == VARIABLE_EXPAND || unesc.at(first_dollar - 1) == VARIABLE_EXPAND_SINGLE)) { first_dollar--; } parse_util_expand_variable_error(unesc, node.source_start, first_dollar, - out_errors); + out_errors); } } diff --git a/src/parser_keywords.cpp b/src/parser_keywords.cpp index 4a99dd94b..f3a531dc6 100644 --- a/src/parser_keywords.cpp +++ b/src/parser_keywords.cpp @@ -1,9 +1,9 @@ // Functions having to do with parser keywords, like testing if a function is a block command. #include "config.h" // IWYU pragma: keep -#include "parser_keywords.h" #include "common.h" #include "fallback.h" // IWYU pragma: keep +#include "parser_keywords.h" bool parser_keywords_skip_arguments(const wcstring &cmd) { return contains(cmd, L"else", L"begin"); diff --git a/src/proc.cpp b/src/proc.cpp index 2e3707543..1a298b210 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -368,9 +368,7 @@ process_t::process_t() { } -process_t::~process_t() { - delete this->next; -} +process_t::~process_t() { delete this->next; } job_t::job_t(job_id_t jobid, const io_chain_t &bio) : block_io(bio), first_process(NULL), pgid(0), tmodes(), job_id(jobid), flags(0) {} diff --git a/src/screen.cpp b/src/screen.cpp index 882bd8d88..08fca25d8 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -505,8 +505,8 @@ static void s_move(screen_t *s, data_buffer_t *b, int new_x, int new_y) { // Use the bulk ('multi') output for cursor movement if it is supported and it would be shorter // Note that this is required to avoid some visual glitches in iTerm (issue #1448). - bool use_multi = multi_str != NULL && multi_str[0] != '\0' && - abs(x_steps) * strlen(str) > strlen(multi_str); + bool use_multi = + multi_str != NULL && multi_str[0] != '\0' && abs(x_steps) * strlen(str) > strlen(multi_str); if (use_multi) { char *multi_param = tparm(multi_str, abs(x_steps)); writembs(multi_param); diff --git a/src/wcstringutil.cpp b/src/wcstringutil.cpp index 7e208469a..163b0e8ed 100644 --- a/src/wcstringutil.cpp +++ b/src/wcstringutil.cpp @@ -1,8 +1,8 @@ // Helper functions for working with wcstring. #include "config.h" // IWYU pragma: keep -#include "wcstringutil.h" #include "common.h" +#include "wcstringutil.h" typedef wcstring::size_type size_type; diff --git a/src/wgetopt.cpp b/src/wgetopt.cpp index 41b7896a1..f7ed54274 100644 --- a/src/wgetopt.cpp +++ b/src/wgetopt.cpp @@ -307,8 +307,8 @@ int wgetopter_t::_wgetopt_internal(int argc, wchar_t **argv, const wchar_t *opts int option_index; for (nameend = nextchar; *nameend && *nameend != '='; nameend++) { - // Do nothing. - } + // Do nothing. + } // Test all long options for either exact match or abbreviated matches. for (p = longopts, option_index = 0; p->name; p++, option_index++) diff --git a/src/wildcard.cpp b/src/wildcard.cpp index b48fe7635..9438ea291 100644 --- a/src/wildcard.cpp +++ b/src/wildcard.cpp @@ -290,9 +290,7 @@ static bool wildcard_complete_internal(const wchar_t *str, const wchar_t *wc, // We don't even try with this one. return false; } - default: { - assert(0 && "Unreachable code reached"); - } + default: { assert(0 && "Unreachable code reached"); } } assert(0 && "Unreachable code reached"); diff --git a/src/wutil.cpp b/src/wutil.cpp index 9f975fab4..3b0bdb2e6 100644 --- a/src/wutil.cpp +++ b/src/wutil.cpp @@ -344,9 +344,9 @@ wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path) { } #if __APPLE__ && __DARWIN_C_LEVEL < 200809L - // OS X Snow Leopard is broken with respect to the dynamically allocated buffer returned by - // realpath(). It's not dynamically allocated so attempting to free that buffer triggers a - // malloc/free error. Thus we don't attempt the free in this case. +// OS X Snow Leopard is broken with respect to the dynamically allocated buffer returned by +// realpath(). It's not dynamically allocated so attempting to free that buffer triggers a +// malloc/free error. Thus we don't attempt the free in this case. #else free(narrow_res); #endif From 980fb592321fc36004a4c72f430b3cd461d95281 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frederik=20=E2=80=9CFreso=E2=80=9D=20S=2E=20Olesen?= Date: Fri, 27 May 2016 12:31:48 +0200 Subject: [PATCH 323/363] Remove executable flag from pacaur completion. --- share/completions/pacaur.fish | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 share/completions/pacaur.fish diff --git a/share/completions/pacaur.fish b/share/completions/pacaur.fish old mode 100755 new mode 100644 From 7af9e1f5c5bfe9b295309094fb03ef27ee6ca1fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frederik=20=E2=80=9CFreso=E2=80=9D=20S=2E=20Olesen?= Date: Fri, 27 May 2016 12:19:01 +0200 Subject: [PATCH 324/363] Split off __fish_complete_blockdevice from mount.fish. The __fish_complete_blockdevice function can be useful to other completions than mount.fish, so it should live on its own so its available to those. --- share/completions/mount.fish | 8 -------- share/functions/__fish_complete_blockdevice.fish | 12 ++++++++++++ 2 files changed, 12 insertions(+), 8 deletions(-) create mode 100644 share/functions/__fish_complete_blockdevice.fish diff --git a/share/completions/mount.fish b/share/completions/mount.fish index ba0d985fc..9375a473e 100644 --- a/share/completions/mount.fish +++ b/share/completions/mount.fish @@ -1,12 +1,4 @@ # Completions for mount -function __fish_complete_blockdevice - set -l cmd (commandline -ct) - [ "" = "$cmd" ]; and return - for f in $cmd* - [ -b $f ]; and printf "%s\t%s\n" $f "Block device" - [ -d $f ]; and printf "%s\n" $f/ - end -end complete -x -c mount -a '(__fish_complete_blockdevice)' # In case `mount UUID=` and similar also works diff --git a/share/functions/__fish_complete_blockdevice.fish b/share/functions/__fish_complete_blockdevice.fish new file mode 100644 index 000000000..d07487dec --- /dev/null +++ b/share/functions/__fish_complete_blockdevice.fish @@ -0,0 +1,12 @@ +# Helper function for completions that need to enumerate block devices. +function __fish_complete_blockdevice + set -l cmd (commandline -ct) + test "" = "$cmd" + and return + for f in $cmd* + test -b $f + and printf "%s\t%s\n" $f "Block device" + test -d $f + and printf "%s\n" $f/ + end +end From 2885c085d8da65156001592cca1bb3e3002b164b Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 25 May 2016 21:23:50 +0200 Subject: [PATCH 325/363] Allow autosuggestion after completion It seems kinda silly to not directly do it, but it was explicitly stopped in the code. I'm quite good at deleting that, aren't I? Fixes #3016. --- src/reader.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/reader.cpp b/src/reader.cpp index 47258c64e..6b23e1291 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -1127,9 +1127,6 @@ static void completion_insert(const wchar_t *val, complete_flags_t flags) { wcstring new_command_line = completion_apply_to_command_line(val, flags, el->text, &cursor, false /* not append only */); reader_set_buffer_maintaining_pager(new_command_line, cursor); - - // Since we just inserted a completion, don't immediately do a new autosuggestion. - data->suppress_autosuggestion = true; } struct autosuggestion_context_t { @@ -2775,15 +2772,15 @@ const wchar_t *reader_readline(int nchars) { // 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(); - // If the user hits return while navigating the pager, it only clears the pager. if (data->is_navigating_pager_contents()) { clear_pager(); break; } + // Delete any autosuggestion. + data->autosuggestion.clear(); + // The user may have hit return with pager contents, but while not navigating them. // Clear the pager in that event. clear_pager(); From ffcfe73299ee384b437cf77a64ae07124583dd52 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 25 May 2016 21:24:37 +0200 Subject: [PATCH 326/363] Allow suggestion when selecting in pager Also mentioned in #3016. --- src/reader.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/reader.cpp b/src/reader.cpp index 6b23e1291..b67f290bc 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -530,9 +530,6 @@ void reader_data_t::pager_selection_changed() { } reader_set_buffer_maintaining_pager(new_cmd_line, cursor_pos); - // Since we just inserted a completion, don't immediately do a new autosuggestion. - this->suppress_autosuggestion = true; - // Trigger repaint (see issue #765). reader_repaint_needed(); } From d79a5a315256193aef880b1f862aed677eb93c18 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 28 May 2016 12:34:04 +0200 Subject: [PATCH 327/363] Funced: Make removal safer, take two Now we try to remove the file and then the directory, without forcing anything, showing any (quite unexpected) error to the user, once. --- share/functions/funced.fish | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/share/functions/funced.fish b/share/functions/funced.fish index 14cb0d70a..fd3060a7e 100644 --- a/share/functions/funced.fish +++ b/share/functions/funced.fish @@ -124,8 +124,7 @@ function funced --description 'Edit function definition' break end set -l stat $status - # Only forcibly delete files to limit possible damage is tmpdir is set to something weird - rm -f $tmpname >/dev/null - rm -r $tmpdir >/dev/null + rm $tmpname >/dev/null + and rmdir $tmpdir >/dev/null return $stat end From ffe5736abbf8644d8ab9af28e116186de62c40ea Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 28 May 2016 14:22:16 +0200 Subject: [PATCH 328/363] History docs: Move descriptions to the corresponding options This should clarify `--delete`s behavior without `--prefix` or `--contains` a bit. Fixes #3054. --- doc_src/history.txt | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/doc_src/history.txt b/doc_src/history.txt index 1bdcdf1e8..fa4908509 100644 --- a/doc_src/history.txt +++ b/doc_src/history.txt @@ -17,19 +17,14 @@ The following options are available: - `--clear` clears the history file. A prompt is displayed before the history is erased. -- `--search` returns history items in keeping with the `--prefix` or `--contains` options. +- `--search` returns history items in keeping with the `--prefix` or `--contains` options. Without either, `--contains` will be assumed. -- `--delete` deletes history items. +- `--delete` deletes history items. Without the `--prefix` or `--contains` options, the exact match will be deleted. With either of these options, a prompt will be displayed before any items are deleted. - `--prefix` searches or deletes items in the history that begin with the specified text string. - `--contains` searches or deletes items in the history that contain the specified text string. -If `--search` is specified without `--contains` or `--prefix`, `--contains` will be assumed. - -If `--delete` is specified without `--contains` or `--prefix`, only a history item which exactly matches the parameter will be erased. No prompt will be given. If `--delete` is specified with either of these parameters, an interactive prompt will be displayed before any items are deleted. - - \subsection history-examples Example \fish From aaaea44714a3fbd1b75d21953ab837be18fc8d4f Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 28 May 2016 17:58:29 +0200 Subject: [PATCH 329/363] Git prompt: Only shorten sha if needed This speeds up the common case when IO is slow, e.g. when used with sshfs. We only use the short sha for figuring out whether the state is valid (for which a long sha should also work) and for display when HEAD is detached (I think that's the correct git-ism). Working towards #3083. --- share/functions/__fish_git_prompt.fish | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/share/functions/__fish_git_prompt.fish b/share/functions/__fish_git_prompt.fish index 128a3f975..9a5be6877 100644 --- a/share/functions/__fish_git_prompt.fish +++ b/share/functions/__fish_git_prompt.fish @@ -351,17 +351,14 @@ function __fish_git_prompt --description "Prompt function for Git" if not command -s git >/dev/null return 1 end - set -l repo_info (command git rev-parse --git-dir --is-inside-git-dir --is-bare-repository --is-inside-work-tree --short HEAD ^/dev/null) + set -l repo_info (command git rev-parse --git-dir --is-inside-git-dir --is-bare-repository --is-inside-work-tree HEAD ^/dev/null) test -n "$repo_info"; or return set -l git_dir $repo_info[1] set -l inside_gitdir $repo_info[2] set -l bare_repo $repo_info[3] set -l inside_worktree $repo_info[4] - set -l short_sha - if test (count $repo_info) = 5 - set short_sha $repo_info[5] - end + set -q repo_info[5]; and set -l sha $repo_info[5] set -l rbc (__fish_git_prompt_operation_branch_bare $repo_info) set -l r $rbc[1] # current operation @@ -388,7 +385,7 @@ function __fish_git_prompt --description "Prompt function for Git" set -l config (command git config --bool bash.showDirtyState) if test "$config" != "false" set w (__fish_git_prompt_dirty) - set i (__fish_git_prompt_staged $short_sha) + set i (__fish_git_prompt_staged $sha) end end @@ -462,11 +459,11 @@ end ### helper functions function __fish_git_prompt_staged --description "__fish_git_prompt helper, tells whether or not the current branch has staged files" - set -l short_sha $argv[1] + set -l sha $argv[1] set -l staged - if test -n "$short_sha" + if test -n "$sha" command git diff-index --cached --quiet HEAD --; or set staged $___fish_git_prompt_char_stagedstate else set staged $___fish_git_prompt_char_invalidstate @@ -534,10 +531,7 @@ function __fish_git_prompt_operation_branch_bare --description "__fish_git_promp set -l git_dir $argv[1] set -l inside_gitdir $argv[2] set -l bare_repo $argv[3] - set -l short_sha - if test (count $argv) = 5 - set short_sha $argv[5] - end + set -q argv[5]; and set -l sha $argv[5] set -l branch set -l operation @@ -598,6 +592,7 @@ function __fish_git_prompt_operation_branch_bare --description "__fish_git_promp command git describe --tags --exact-match HEAD end ^/dev/null; set os $status) if test $os -ne 0 + set -q sha; and set -l short_sha (command git rev-parse --short $sha) if test -n "$short_sha" set branch $short_sha... else From 8e88b29eeb9aa7c42a00a4188c6c26a17d8ad9f5 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 28 May 2016 19:30:29 +0200 Subject: [PATCH 330/363] Git prompt: Shorten the sha ourselves Possibly fixes #3083. --- share/functions/__fish_git_prompt.fish | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/share/functions/__fish_git_prompt.fish b/share/functions/__fish_git_prompt.fish index 9a5be6877..4073434b0 100644 --- a/share/functions/__fish_git_prompt.fish +++ b/share/functions/__fish_git_prompt.fish @@ -592,9 +592,10 @@ function __fish_git_prompt_operation_branch_bare --description "__fish_git_promp command git describe --tags --exact-match HEAD end ^/dev/null; set os $status) if test $os -ne 0 - set -q sha; and set -l short_sha (command git rev-parse --short $sha) - if test -n "$short_sha" - set branch $short_sha... + # Shorten the sha ourselves to 8 characters - this should be good for most repositories, + # and even for large ones it should be good for most commits + if set -q sha + set branch (string match -r '^.{8}' -- $sha)... else set branch unknown end From a918397da263ed61d78e2acb3aac1502944cf7fe Mon Sep 17 00:00:00 2001 From: Wieland Hoffmann Date: Sun, 29 May 2016 12:24:24 +0200 Subject: [PATCH 331/363] It's `status --is-interactive` (#3086) --- etc/config.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/config.fish b/etc/config.fish index 9be6f0728..f4cb09baf 100644 --- a/etc/config.fish +++ b/etc/config.fish @@ -9,6 +9,6 @@ # ... # end # To include configuration only for interactive shells, use -# if status --is-interactiv +# if status --is-interactive # ... # end From 2871096f9c87bc561de17e90939fa1727f6ce186 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 29 May 2016 13:58:26 +0200 Subject: [PATCH 332/363] git completions: Add general options These are the options between `git` and the subcommand. Fixes #3087. --- share/completions/git.fish | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/share/completions/git.fish b/share/completions/git.fish index 3f2a3b1ce..dd3cc46ba 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -205,6 +205,26 @@ end # general options complete -f -c git -l help -d 'Display the manual of a git command' +complete -f -c git -l version -d 'Display version' +complete -x -c git -s C -a '(__fish_complete_directories)' -d 'Run as if git was started in this directory' +complete -x -c git -s c -a '(command git config -l | string replace = \t)' -d 'Set a configuration option' +complete -x -c git -l exec-path -a '(__fish_git_complete_directories)' -d 'Get or set the path to the git programs' +complete -f -c git -l html-path -d 'Print the path to the html documentation' +complete -f -c git -l man-path -d 'Print the path to the man documentation' +complete -f -c git -l info-path -d 'Print the path to the info documentation' +complete -f -c git -s p -l paginate -d 'Pipe output into a pager' +complete -f -c git -l no-pager -d 'Do not pipe output into a pager' +complete -f -c git -l git-dir -d 'Set the path to the repository' +complete -f -c git -l work-tree -d 'Set the path to the working tree' +complete -f -c git -l namespace -d 'Set the namespace' +complete -f -c git -l bare -d 'Treat the repository as bare' +complete -f -c git -l no-replace-objects -d 'Do not use replacement refs to replace git objects' +complete -f -c git -l literal-pathspecs -d 'Treat pathspecs literally' +complete -f -c git -l glob-pathspecs -d 'Treat pathspecs as globs' +complete -f -c git -l noglob-pathspecs -d "Don't treat pathspecs as globs" +complete -f -c git -l icase-pathspecs -d 'Match pathspecs case-insensitively' + +# Options shared between multiple commands complete -f -c git -n '__fish_git_using_command log show diff-tree rev-list' -l pretty -a 'oneline short medium full fuller email raw format:' #### fetch From 763c620d0b55ad0ea8c578e84104b3ae331393d5 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 29 May 2016 14:06:12 +0200 Subject: [PATCH 333/363] Stringify git prompt --- share/functions/__fish_git_prompt.fish | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/share/functions/__fish_git_prompt.fish b/share/functions/__fish_git_prompt.fish index 4073434b0..ced413579 100644 --- a/share/functions/__fish_git_prompt.fish +++ b/share/functions/__fish_git_prompt.fish @@ -189,7 +189,7 @@ function __fish_git_prompt_show_upstream --description "Helper function for __fi set -l svn_remote # get some config options from git-config - command git config -z --get-regexp '^(svn-remote\..*\.url|bash\.showupstream)$' ^/dev/null | tr '\0\n' '\n ' | while read -l key value + command git config -z --get-regexp '^(svn-remote\..*\.url|bash\.showupstream)$' ^/dev/null | while read -lz key value switch $key case bash.showupstream set show_upstream $value @@ -198,14 +198,14 @@ function __fish_git_prompt_show_upstream --description "Helper function for __fi set svn_remote $svn_remote $value # Avoid adding \| to the beginning to avoid needing #?? later if test -n "$svn_url_pattern" - set svn_url_pattern $svn_url_pattern"\\|$value" + set svn_url_pattern $svn_url_pattern"|$value" else set svn_url_pattern $value end set upstream svn+git # default upstream is SVN if available, else git # Save the config key (without .url) for later use - set -l remote_prefix (echo $key | sed 's/\.url$//') + set -l remote_prefix (string replace -r '\.url$' '' -- $key) set svn_prefix $svn_prefix $remote_prefix end end @@ -242,11 +242,11 @@ function __fish_git_prompt_show_upstream --description "Helper function for __fi set -l svn_upstream (git log --first-parent -1 --grep="^git-svn-id: \($svn_url_pattern\)" ^/dev/null) if test (count $svn_upstream) -ne 0 echo $svn_upstream[-1] | read -l __ svn_upstream __ - set svn_upstream (echo $svn_upstream | sed 's/@.*//') + set svn_upstream (string replace -r '@.*' '' -- $svn_upstream) set -l cur_prefix for i in (seq (count $svn_remote)) set -l remote $svn_remote[$i] - set -l mod_upstream (echo $svn_upstream | sed "s|$remote||") + set -l mod_upstream (string replace "$remote" "" -- $svn_upstream) if test "$svn_upstream" != "$mod_upstream" # we found a valid remote set svn_upstream $mod_upstream @@ -263,14 +263,14 @@ function __fish_git_prompt_show_upstream --description "Helper function for __fi set upstream git-svn end else - set upstream (echo $svn_upstream | sed 's|/branches||; s|/||g') + set upstream (string replace '/branches' '' -- $svn_upstream | string replace -a '/' '') # Use fetch config to fix upstream set -l fetch_val (command git config "$cur_prefix".fetch) if test -n "$fetch_val" set -l IFS : echo "$fetch_val" | read -l trunk pattern - set upstream (echo $pattern | sed -e "s|/$trunk\$||") /$upstream + set upstream (string replace -r -- "/$trunk\$" '' $pattern) /$upstream end end else if test $upstream = svn+git @@ -429,7 +429,7 @@ function __fish_git_prompt --description "Prompt function for Git" if test -n "$u" set u "$___fish_git_prompt_color_untrackedfiles$u$___fish_git_prompt_color_untrackedfiles_done" end - set b (echo $b | sed 's|refs/heads/||') + set b (string replace refs/heads/ '' -- $b) if test -n "$b" set b "$branch_color$b$branch_done" end From 0882e0cb9550e1f12c69f51032241158c6148e03 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 29 May 2016 14:07:15 +0200 Subject: [PATCH 334/363] Git prompt: Remove legacy option Git has supported `rev-list --count` for years, so this shouldn't be needed anymore. --- share/functions/__fish_git_prompt.fish | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/share/functions/__fish_git_prompt.fish b/share/functions/__fish_git_prompt.fish index ced413579..909dd6bd3 100644 --- a/share/functions/__fish_git_prompt.fish +++ b/share/functions/__fish_git_prompt.fish @@ -53,8 +53,6 @@ # verbose show number of commits ahead/behind (+/-) upstream # name if verbose, then also show the upstream abbrev name # informative similar to verbose, but shows nothing when equal (fish only) -# legacy don't use the '--count' option available in recent versions -# of git-rev-list # git always compare HEAD to @{upstream} # svn always compare HEAD to your SVN upstream # none disables (fish only, useful with show_informative_status) @@ -178,7 +176,6 @@ function __fish_git_prompt_show_upstream --description "Helper function for __fi set -l svn_url_pattern set -l count set -l upstream git - set -l legacy set -l verbose set -l name @@ -222,9 +219,6 @@ function __fish_git_prompt_show_upstream --description "Helper function for __fi set -e informative case informative set informative 1 - case legacy - set legacy 1 - set -e informative case name set name 1 case none @@ -279,20 +273,7 @@ function __fish_git_prompt_show_upstream --description "Helper function for __fi end # Find how many commits we are ahead/behind our upstream - if test -z "$legacy" - set count (command git rev-list --count --left-right $upstream...HEAD ^/dev/null) - else - # produce equivalent output to --count for older versions of git - set -l os - set -l commits (command git rev-list --left-right $upstream...HEAD ^/dev/null; set os $status) - if test $os -eq 0 - set -l behind (count (for arg in $commits; echo $arg; end | string match -r '^<')) - set -l ahead (count (for arg in $commits; echo $arg; end | string match -r -v '^<')) - set count "$behind $ahead" - else - set count - end - end + set count (command git rev-list --count --left-right $upstream...HEAD ^/dev/null) # calculate the result if test -n "$verbose" From f23464001f40a52f2a7aa84a9c16c0fed3e3c3ca Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 29 May 2016 14:10:10 +0200 Subject: [PATCH 335/363] Indent __fish_git_prompt --- share/functions/__fish_git_prompt.fish | 937 +++++++++++++------------ 1 file changed, 471 insertions(+), 466 deletions(-) diff --git a/share/functions/__fish_git_prompt.fish b/share/functions/__fish_git_prompt.fish index 909dd6bd3..9591ef386 100644 --- a/share/functions/__fish_git_prompt.fish +++ b/share/functions/__fish_git_prompt.fish @@ -169,400 +169,405 @@ # flags Defaults to --bold blue function __fish_git_prompt_show_upstream --description "Helper function for __fish_git_prompt" - set -l show_upstream $__fish_git_prompt_showupstream - set -l svn_prefix # For better SVN upstream information - set -l informative + set -l show_upstream $__fish_git_prompt_showupstream + set -l svn_prefix # For better SVN upstream information + set -l informative - set -l svn_url_pattern - set -l count - set -l upstream git - set -l verbose - set -l name + set -l svn_url_pattern + set -l count + set -l upstream git + set -l verbose + set -l name - # Default to informative if show_informative_status is set - if test -n "$__fish_git_prompt_show_informative_status" - set informative 1 - end + # Default to informative if show_informative_status is set + if test -n "$__fish_git_prompt_show_informative_status" + set informative 1 + end - set -l svn_remote - # get some config options from git-config - command git config -z --get-regexp '^(svn-remote\..*\.url|bash\.showupstream)$' ^/dev/null | while read -lz key value - switch $key - case bash.showupstream - set show_upstream $value - test -n "$show_upstream"; or return - case svn-remote.'*'.url - set svn_remote $svn_remote $value - # Avoid adding \| to the beginning to avoid needing #?? later - if test -n "$svn_url_pattern" - set svn_url_pattern $svn_url_pattern"|$value" - else - set svn_url_pattern $value - end - set upstream svn+git # default upstream is SVN if available, else git + set -l svn_remote + # get some config options from git-config + command git config -z --get-regexp '^(svn-remote\..*\.url|bash\.showupstream)$' ^/dev/null | while read -lz key value + switch $key + case bash.showupstream + set show_upstream $value + test -n "$show_upstream" + or return + case svn-remote.'*'.url + set svn_remote $svn_remote $value + # Avoid adding \| to the beginning to avoid needing #?? later + if test -n "$svn_url_pattern" + set svn_url_pattern $svn_url_pattern"|$value" + else + set svn_url_pattern $value + end + set upstream svn+git # default upstream is SVN if available, else git - # Save the config key (without .url) for later use - set -l remote_prefix (string replace -r '\.url$' '' -- $key) - set svn_prefix $svn_prefix $remote_prefix - end - end + # Save the config key (without .url) for later use + set -l remote_prefix (string replace -r '\.url$' '' -- $key) + set svn_prefix $svn_prefix $remote_prefix + end + end - # parse configuration variables - # and clear informative default when needed - for option in $show_upstream - switch $option - case git svn - set upstream $option - set -e informative - case verbose - set verbose 1 - set -e informative - case informative - set informative 1 - case name - set name 1 - case none - return - end - end + # parse configuration variables + # and clear informative default when needed + for option in $show_upstream + switch $option + case git svn + set upstream $option + set -e informative + case verbose + set verbose 1 + set -e informative + case informative + set informative 1 + case name + set name 1 + case none + return + end + end - # Find our upstream - switch $upstream - case git - set upstream '@{upstream}' - case svn\* - # get the upstream from the 'git-svn-id: ...' in a commit message - # (git-svn uses essentially the same procedure internally) - set -l svn_upstream (git log --first-parent -1 --grep="^git-svn-id: \($svn_url_pattern\)" ^/dev/null) - if test (count $svn_upstream) -ne 0 - echo $svn_upstream[-1] | read -l __ svn_upstream __ - set svn_upstream (string replace -r '@.*' '' -- $svn_upstream) - set -l cur_prefix - for i in (seq (count $svn_remote)) - set -l remote $svn_remote[$i] - set -l mod_upstream (string replace "$remote" "" -- $svn_upstream) - if test "$svn_upstream" != "$mod_upstream" - # we found a valid remote - set svn_upstream $mod_upstream - set cur_prefix $svn_prefix[$i] - break - end - end + # Find our upstream + switch $upstream + case git + set upstream '@{upstream}' + case svn\* + # get the upstream from the 'git-svn-id: ...' in a commit message + # (git-svn uses essentially the same procedure internally) + set -l svn_upstream (git log --first-parent -1 --grep="^git-svn-id: \($svn_url_pattern\)" ^/dev/null) + if test (count $svn_upstream) -ne 0 + echo $svn_upstream[-1] | read -l __ svn_upstream __ + set svn_upstream (string replace -r '@.*' '' -- $svn_upstream) + set -l cur_prefix + for i in (seq (count $svn_remote)) + set -l remote $svn_remote[$i] + set -l mod_upstream (string replace "$remote" "" -- $svn_upstream) + if test "$svn_upstream" != "$mod_upstream" + # we found a valid remote + set svn_upstream $mod_upstream + set cur_prefix $svn_prefix[$i] + break + end + end - if test -z "$svn_upstream" - # default branch name for checkouts with no layout: - if test -n "$GIT_SVN_ID" - set upstream $GIT_SVN_ID - else - set upstream git-svn - end - else - set upstream (string replace '/branches' '' -- $svn_upstream | string replace -a '/' '') + if test -z "$svn_upstream" + # default branch name for checkouts with no layout: + if test -n "$GIT_SVN_ID" + set upstream $GIT_SVN_ID + else + set upstream git-svn + end + else + set upstream (string replace '/branches' '' -- $svn_upstream | string replace -a '/' '') - # Use fetch config to fix upstream - set -l fetch_val (command git config "$cur_prefix".fetch) - if test -n "$fetch_val" - set -l IFS : - echo "$fetch_val" | read -l trunk pattern - set upstream (string replace -r -- "/$trunk\$" '' $pattern) /$upstream - end - end - else if test $upstream = svn+git - set upstream '@{upstream}' - end - end + # Use fetch config to fix upstream + set -l fetch_val (command git config "$cur_prefix".fetch) + if test -n "$fetch_val" + set -l IFS : + echo "$fetch_val" | read -l trunk pattern + set upstream (string replace -r -- "/$trunk\$" '' $pattern) /$upstream + end + end + else if test $upstream = svn+git + set upstream '@{upstream}' + end + end - # Find how many commits we are ahead/behind our upstream - set count (command git rev-list --count --left-right $upstream...HEAD ^/dev/null) + # Find how many commits we are ahead/behind our upstream + set count (command git rev-list --count --left-right $upstream...HEAD ^/dev/null) - # calculate the result - if test -n "$verbose" - # Verbose has a space by default - set -l prefix "$___fish_git_prompt_char_upstream_prefix" - # Using two underscore version to check if user explicitly set to nothing - if not set -q __fish_git_prompt_char_upstream_prefix - set -l prefix " " - end + # calculate the result + if test -n "$verbose" + # Verbose has a space by default + set -l prefix "$___fish_git_prompt_char_upstream_prefix" + # Using two underscore version to check if user explicitly set to nothing + if not set -q __fish_git_prompt_char_upstream_prefix + set -l prefix " " + end - echo $count | read -l behind ahead - switch "$count" - case '' # no upstream - case "0 0" # equal to upstream - echo "$prefix$___fish_git_prompt_char_upstream_equal" - case "0 *" # ahead of upstream - echo "$prefix$___fish_git_prompt_char_upstream_ahead$ahead" - case "* 0" # behind upstream - echo "$prefix$___fish_git_prompt_char_upstream_behind$behind" - case '*' # diverged from upstream - echo "$prefix$___fish_git_prompt_char_upstream_diverged$ahead-$behind" - end - if test -n "$count" -a -n "$name" - echo " "(command git rev-parse --abbrev-ref "$upstream" ^/dev/null) - end - else if test -n "$informative" - echo $count | read -l behind ahead - switch "$count" - case '' # no upstream - case "0 0" # equal to upstream - case "0 *" # ahead of upstream - echo "$___fish_git_prompt_char_upstream_prefix$___fish_git_prompt_char_upstream_ahead$ahead" - case "* 0" # behind upstream - echo "$___fish_git_prompt_char_upstream_prefix$___fish_git_prompt_char_upstream_behind$behind" - case '*' # diverged from upstream - echo "$___fish_git_prompt_char_upstream_prefix$___fish_git_prompt_char_upstream_ahead$ahead$___fish_git_prompt_char_upstream_behind$behind" - end - else - switch "$count" - case '' # no upstream - case "0 0" # equal to upstream - echo "$___fish_git_prompt_char_upstream_prefix$___fish_git_prompt_char_upstream_equal" - case "0 *" # ahead of upstream - echo "$___fish_git_prompt_char_upstream_prefix$___fish_git_prompt_char_upstream_ahead" - case "* 0" # behind upstream - echo "$___fish_git_prompt_char_upstream_prefix$___fish_git_prompt_char_upstream_behind" - case '*' # diverged from upstream - echo "$___fish_git_prompt_char_upstream_prefix$___fish_git_prompt_char_upstream_diverged" - end - end + echo $count | read -l behind ahead + switch "$count" + case '' # no upstream + case "0 0" # equal to upstream + echo "$prefix$___fish_git_prompt_char_upstream_equal" + case "0 *" # ahead of upstream + echo "$prefix$___fish_git_prompt_char_upstream_ahead$ahead" + case "* 0" # behind upstream + echo "$prefix$___fish_git_prompt_char_upstream_behind$behind" + case '*' # diverged from upstream + echo "$prefix$___fish_git_prompt_char_upstream_diverged$ahead-$behind" + end + if test -n "$count" -a -n "$name" + echo " "(command git rev-parse --abbrev-ref "$upstream" ^/dev/null) + end + else if test -n "$informative" + echo $count | read -l behind ahead + switch "$count" + case '' # no upstream + case "0 0" # equal to upstream + case "0 *" # ahead of upstream + echo "$___fish_git_prompt_char_upstream_prefix$___fish_git_prompt_char_upstream_ahead$ahead" + case "* 0" # behind upstream + echo "$___fish_git_prompt_char_upstream_prefix$___fish_git_prompt_char_upstream_behind$behind" + case '*' # diverged from upstream + echo "$___fish_git_prompt_char_upstream_prefix$___fish_git_prompt_char_upstream_ahead$ahead$___fish_git_prompt_char_upstream_behind$behind" + end + else + switch "$count" + case '' # no upstream + case "0 0" # equal to upstream + echo "$___fish_git_prompt_char_upstream_prefix$___fish_git_prompt_char_upstream_equal" + case "0 *" # ahead of upstream + echo "$___fish_git_prompt_char_upstream_prefix$___fish_git_prompt_char_upstream_ahead" + case "* 0" # behind upstream + echo "$___fish_git_prompt_char_upstream_prefix$___fish_git_prompt_char_upstream_behind" + case '*' # diverged from upstream + echo "$___fish_git_prompt_char_upstream_prefix$___fish_git_prompt_char_upstream_diverged" + end + end end function __fish_git_prompt --description "Prompt function for Git" - # If git isn't installed, there's nothing we can do - # Return 1 so the calling prompt can deal with it - if not command -s git >/dev/null - return 1 - end - set -l repo_info (command git rev-parse --git-dir --is-inside-git-dir --is-bare-repository --is-inside-work-tree HEAD ^/dev/null) - test -n "$repo_info"; or return + # If git isn't installed, there's nothing we can do + # Return 1 so the calling prompt can deal with it + if not command -s git >/dev/null + return 1 + end + set -l repo_info (command git rev-parse --git-dir --is-inside-git-dir --is-bare-repository --is-inside-work-tree HEAD ^/dev/null) + test -n "$repo_info" + or return - set -l git_dir $repo_info[1] - set -l inside_gitdir $repo_info[2] - set -l bare_repo $repo_info[3] - set -l inside_worktree $repo_info[4] - set -q repo_info[5]; and set -l sha $repo_info[5] + set -l git_dir $repo_info[1] + set -l inside_gitdir $repo_info[2] + set -l bare_repo $repo_info[3] + set -l inside_worktree $repo_info[4] + set -q repo_info[5] + and set -l sha $repo_info[5] - set -l rbc (__fish_git_prompt_operation_branch_bare $repo_info) - set -l r $rbc[1] # current operation - set -l b $rbc[2] # current branch - set -l detached $rbc[3] - set -l w #dirty working directory - set -l i #staged changes - set -l s #stashes - set -l u #untracked - set -l c $rbc[4] # bare repository - set -l p #upstream - set -l informative_status + set -l rbc (__fish_git_prompt_operation_branch_bare $repo_info) + set -l r $rbc[1] # current operation + set -l b $rbc[2] # current branch + set -l detached $rbc[3] + set -l w #dirty working directory + set -l i #staged changes + set -l s #stashes + set -l u #untracked + set -l c $rbc[4] # bare repository + set -l p #upstream + set -l informative_status - __fish_git_prompt_validate_chars - __fish_git_prompt_validate_colors + __fish_git_prompt_validate_chars + __fish_git_prompt_validate_colors - set -l space "$___fish_git_prompt_color$___fish_git_prompt_char_stateseparator$___fish_git_prompt_color_done" + set -l space "$___fish_git_prompt_color$___fish_git_prompt_char_stateseparator$___fish_git_prompt_color_done" - if test "true" = $inside_worktree - if test -n "$__fish_git_prompt_show_informative_status" - set informative_status "$space"(__fish_git_prompt_informative_status) - else - if test -n "$__fish_git_prompt_showdirtystate" - set -l config (command git config --bool bash.showDirtyState) - if test "$config" != "false" - set w (__fish_git_prompt_dirty) - set i (__fish_git_prompt_staged $sha) - end - end + if test "true" = $inside_worktree + if test -n "$__fish_git_prompt_show_informative_status" + set informative_status "$space"(__fish_git_prompt_informative_status) + else + if test -n "$__fish_git_prompt_showdirtystate" + set -l config (command git config --bool bash.showDirtyState) + if test "$config" != "false" + set w (__fish_git_prompt_dirty) + set i (__fish_git_prompt_staged $sha) + end + end - if test -n "$__fish_git_prompt_showstashstate" -a -r $git_dir/refs/stash - set s $___fish_git_prompt_char_stashstate - end + if test -n "$__fish_git_prompt_showstashstate" -a -r $git_dir/refs/stash + set s $___fish_git_prompt_char_stashstate + end - if test -n "$__fish_git_prompt_showuntrackedfiles" - set -l config (command git config --bool bash.showUntrackedFiles) - if test "$config" != false - if command git ls-files --others --exclude-standard --error-unmatch -- '*' >/dev/null ^/dev/null - set u $___fish_git_prompt_char_untrackedfiles - end - end - end - end + if test -n "$__fish_git_prompt_showuntrackedfiles" + set -l config (command git config --bool bash.showUntrackedFiles) + if test "$config" != false + if command git ls-files --others --exclude-standard --error-unmatch -- '*' >/dev/null ^/dev/null + set u $___fish_git_prompt_char_untrackedfiles + end + end + end + end - if test -n "$__fish_git_prompt_showupstream" -o "$__fish_git_prompt_show_informative_status" - set p (__fish_git_prompt_show_upstream) - end - end + if test -n "$__fish_git_prompt_showupstream" -o "$__fish_git_prompt_show_informative_status" + set p (__fish_git_prompt_show_upstream) + end + end - set -l branch_color $___fish_git_prompt_color_branch - set -l branch_done $___fish_git_prompt_color_branch_done - if test -n "$__fish_git_prompt_showcolorhints" - if test $detached = yes - set branch_color $___fish_git_prompt_color_branch_detached - set branch_done $___fish_git_prompt_color_branch_detached_done - end - end + set -l branch_color $___fish_git_prompt_color_branch + set -l branch_done $___fish_git_prompt_color_branch_done + if test -n "$__fish_git_prompt_showcolorhints" + if test $detached = yes + set branch_color $___fish_git_prompt_color_branch_detached + set branch_done $___fish_git_prompt_color_branch_detached_done + end + end - if test -n "$w" - set w "$___fish_git_prompt_color_dirtystate$w$___fish_git_prompt_color_dirtystate_done" - end - if test -n "$i" - set i "$___fish_git_prompt_color_stagedstate$i$___fish_git_prompt_color_stagedstate_done" - end - if test -n "$s" - set s "$___fish_git_prompt_color_stashstate$s$___fish_git_prompt_color_stashstate_done" - end - if test -n "$u" - set u "$___fish_git_prompt_color_untrackedfiles$u$___fish_git_prompt_color_untrackedfiles_done" - end - set b (string replace refs/heads/ '' -- $b) - if test -n "$b" - set b "$branch_color$b$branch_done" - end - if test -n "$c" - set c "$___fish_git_prompt_color_bare$c$___fish_git_prompt_color_bare_done" - end - if test -n "$r" - set r "$___fish_git_prompt_color_merging$r$___fish_git_prompt_color_merging_done" - end - if test -n "$p" - set p "$___fish_git_prompt_color_upstream$p$___fish_git_prompt_color_upstream_done" - end + if test -n "$w" + set w "$___fish_git_prompt_color_dirtystate$w$___fish_git_prompt_color_dirtystate_done" + end + if test -n "$i" + set i "$___fish_git_prompt_color_stagedstate$i$___fish_git_prompt_color_stagedstate_done" + end + if test -n "$s" + set s "$___fish_git_prompt_color_stashstate$s$___fish_git_prompt_color_stashstate_done" + end + if test -n "$u" + set u "$___fish_git_prompt_color_untrackedfiles$u$___fish_git_prompt_color_untrackedfiles_done" + end + set b (string replace refs/heads/ '' -- $b) + if test -n "$b" + set b "$branch_color$b$branch_done" + end + if test -n "$c" + set c "$___fish_git_prompt_color_bare$c$___fish_git_prompt_color_bare_done" + end + if test -n "$r" + set r "$___fish_git_prompt_color_merging$r$___fish_git_prompt_color_merging_done" + end + if test -n "$p" + set p "$___fish_git_prompt_color_upstream$p$___fish_git_prompt_color_upstream_done" + end - # Formatting - set -l f "$w$i$s$u" - if test -n "$f" - set f "$space$f" - end - set -l format $argv[1] - if test -z "$format" - set format " (%s)" - end + # Formatting + set -l f "$w$i$s$u" + if test -n "$f" + set f "$space$f" + end + set -l format $argv[1] + if test -z "$format" + set format " (%s)" + end - printf "%s$format%s" "$___fish_git_prompt_color_prefix" "$___fish_git_prompt_color_prefix_done$c$b$f$r$p$informative_status$___fish_git_prompt_color_suffix" "$___git_ps_color_suffix_done" + printf "%s$format%s" "$___fish_git_prompt_color_prefix" "$___fish_git_prompt_color_prefix_done$c$b$f$r$p$informative_status$___fish_git_prompt_color_suffix" "$___git_ps_color_suffix_done" end ### helper functions function __fish_git_prompt_staged --description "__fish_git_prompt helper, tells whether or not the current branch has staged files" - set -l sha $argv[1] + set -l sha $argv[1] - set -l staged + set -l staged - if test -n "$sha" - command git diff-index --cached --quiet HEAD --; or set staged $___fish_git_prompt_char_stagedstate - else - set staged $___fish_git_prompt_char_invalidstate - end - echo $staged + if test -n "$sha" + command git diff-index --cached --quiet HEAD -- + or set staged $___fish_git_prompt_char_stagedstate + else + set staged $___fish_git_prompt_char_invalidstate + end + echo $staged end function __fish_git_prompt_dirty --description "__fish_git_prompt helper, tells whether or not the current branch has tracked, modified files" - set -l dirty + set -l dirty - set -l os - command git diff --no-ext-diff --quiet --exit-code - set os $status - if test $os -ne 0 - set dirty $___fish_git_prompt_char_dirtystate - end - echo $dirty + set -l os + command git diff --no-ext-diff --quiet --exit-code + set os $status + if test $os -ne 0 + set dirty $___fish_git_prompt_char_dirtystate + end + echo $dirty end set -g ___fish_git_prompt_status_order stagedstate invalidstate dirtystate untrackedfiles -function __fish_git_prompt_informative_status +function __fish_git_prompt_informative_status - set -l changedFiles (command git diff --name-status | cut -c 1-2) - set -l stagedFiles (command git diff --staged --name-status | cut -c 1-2) + set -l changedFiles (command git diff --name-status | cut -c 1-2) + set -l stagedFiles (command git diff --staged --name-status | cut -c 1-2) - set -l dirtystate (math (count $changedFiles) - (count (echo $changedFiles | grep "U"))) - set -l invalidstate (count (echo $stagedFiles | grep "U")) - set -l stagedstate (math (count $stagedFiles) - $invalidstate) - set -l untrackedfiles (count (command git ls-files --others --exclude-standard)) + set -l dirtystate (math (count $changedFiles) - (count (echo $changedFiles | grep "U"))) + set -l invalidstate (count (echo $stagedFiles | grep "U")) + set -l stagedstate (math (count $stagedFiles) - $invalidstate) + set -l untrackedfiles (count (command git ls-files --others --exclude-standard)) - set -l info + set -l info - if [ (math $dirtystate + $invalidstate + $stagedstate + $untrackedfiles) = 0 ] - set info $___fish_git_prompt_color_cleanstate$___fish_git_prompt_char_cleanstate$___fish_git_prompt_color_cleanstate_done - else - for i in $___fish_git_prompt_status_order - if [ $$i != "0" ] - set -l color_var ___fish_git_prompt_color_$i - set -l color_done_var ___fish_git_prompt_color_{$i}_done - set -l symbol_var ___fish_git_prompt_char_$i + if [ (math $dirtystate + $invalidstate + $stagedstate + $untrackedfiles) = 0 ] + set info $___fish_git_prompt_color_cleanstate$___fish_git_prompt_char_cleanstate$___fish_git_prompt_color_cleanstate_done + else + for i in $___fish_git_prompt_status_order + if [ $$i != "0" ] + set -l color_var ___fish_git_prompt_color_$i + set -l color_done_var ___fish_git_prompt_color_{$i}_done + set -l symbol_var ___fish_git_prompt_char_$i - set -l color $$color_var - set -l color_done $$color_done_var - set -l symbol $$symbol_var + set -l color $$color_var + set -l color_done $$color_done_var + set -l symbol $$symbol_var - set -l count + set -l count - if not set -q __fish_git_prompt_hide_$i - set count $$i - end + if not set -q __fish_git_prompt_hide_$i + set count $$i + end - set info "$info$color$symbol$count$color_done" - end - end - end + set info "$info$color$symbol$count$color_done" + end + end + end - echo $info + echo $info end # Keeping these together avoids many duplicated checks function __fish_git_prompt_operation_branch_bare --description "__fish_git_prompt helper, returns the current Git operation and branch" - # This function is passed the full repo_info array - set -l git_dir $argv[1] - set -l inside_gitdir $argv[2] - set -l bare_repo $argv[3] - set -q argv[5]; and set -l sha $argv[5] + # This function is passed the full repo_info array + set -l git_dir $argv[1] + set -l inside_gitdir $argv[2] + set -l bare_repo $argv[3] + set -q argv[5] + and set -l sha $argv[5] - set -l branch - set -l operation - set -l detached no - set -l bare - set -l step - set -l total - set -l os + set -l branch + set -l operation + set -l detached no + set -l bare + set -l step + set -l total + set -l os - if test -d $git_dir/rebase-merge - set branch (cat $git_dir/rebase-merge/head-name ^/dev/null) - set step (cat $git_dir/rebase-merge/msgnum ^/dev/null) - set total (cat $git_dir/rebase-merge/end ^/dev/null) - if test -f $git_dir/rebase-merge/interactive - set operation "|REBASE-i" - else - set operation "|REBASE-m" - end - else - if test -d $git_dir/rebase-apply - set step (cat $git_dir/rebase-apply/next ^/dev/null) - set total (cat $git_dir/rebase-apply/last ^/dev/null) - if test -f $git_dir/rebase-apply/rebasing - set branch (cat $git_dir/rebase-apply/head-name ^/dev/null) - set operation "|REBASE" - else if test -f $git_dir/rebase-apply/applying - set operation "|AM" - else - set operation "|AM/REBASE" - end - else if test -f $git_dir/MERGE_HEAD - set operation "|MERGING" - else if test -f $git_dir/CHERRY_PICK_HEAD - set operation "|CHERRY-PICKING" - else if test -f $git_dir/REVERT_HEAD - set operation "|REVERTING" - else if test -f $git_dir/BISECT_LOG - set operation "|BISECTING" - end - end + if test -d $git_dir/rebase-merge + set branch (cat $git_dir/rebase-merge/head-name ^/dev/null) + set step (cat $git_dir/rebase-merge/msgnum ^/dev/null) + set total (cat $git_dir/rebase-merge/end ^/dev/null) + if test -f $git_dir/rebase-merge/interactive + set operation "|REBASE-i" + else + set operation "|REBASE-m" + end + else + if test -d $git_dir/rebase-apply + set step (cat $git_dir/rebase-apply/next ^/dev/null) + set total (cat $git_dir/rebase-apply/last ^/dev/null) + if test -f $git_dir/rebase-apply/rebasing + set branch (cat $git_dir/rebase-apply/head-name ^/dev/null) + set operation "|REBASE" + else if test -f $git_dir/rebase-apply/applying + set operation "|AM" + else + set operation "|AM/REBASE" + end + else if test -f $git_dir/MERGE_HEAD + set operation "|MERGING" + else if test -f $git_dir/CHERRY_PICK_HEAD + set operation "|CHERRY-PICKING" + else if test -f $git_dir/REVERT_HEAD + set operation "|REVERTING" + else if test -f $git_dir/BISECT_LOG + set operation "|BISECTING" + end + end - if test -n "$step" -a -n "$total" - set operation "$operation $step/$total" - end + if test -n "$step" -a -n "$total" + set operation "$operation $step/$total" + end - if test -z "$branch" - set branch (command git symbolic-ref HEAD ^/dev/null; set os $status) - if test $os -ne 0 - set detached yes - set branch (switch "$__fish_git_prompt_describe_style" + if test -z "$branch" + set branch (command git symbolic-ref HEAD ^/dev/null; set os $status) + if test $os -ne 0 + set detached yes + set branch (switch "$__fish_git_prompt_describe_style" case contains command git describe --contains HEAD case branch @@ -572,185 +577,185 @@ function __fish_git_prompt_operation_branch_bare --description "__fish_git_promp case default '*' command git describe --tags --exact-match HEAD end ^/dev/null; set os $status) - if test $os -ne 0 - # Shorten the sha ourselves to 8 characters - this should be good for most repositories, - # and even for large ones it should be good for most commits - if set -q sha - set branch (string match -r '^.{8}' -- $sha)... - else - set branch unknown - end - end - set branch "($branch)" - end - end + if test $os -ne 0 + # Shorten the sha ourselves to 8 characters - this should be good for most repositories, + # and even for large ones it should be good for most commits + if set -q sha + set branch (string match -r '^.{8}' -- $sha)... + else + set branch unknown + end + end + set branch "($branch)" + end + end - if test "true" = $inside_gitdir - if test "true" = $bare_repo - set bare "BARE:" - else - # Let user know they're inside the git dir of a non-bare repo - set branch "GIT_DIR!" - end - end + if test "true" = $inside_gitdir + if test "true" = $bare_repo + set bare "BARE:" + else + # Let user know they're inside the git dir of a non-bare repo + set branch "GIT_DIR!" + end + end - echo $operation - echo $branch - echo $detached - echo $bare + echo $operation + echo $branch + echo $detached + echo $bare end function __fish_git_prompt_set_char - set -l user_variable_name "$argv[1]" - set -l char $argv[2] - set -l user_variable $$user_variable_name + set -l user_variable_name "$argv[1]" + set -l char $argv[2] + set -l user_variable $$user_variable_name - if test (count $argv) -ge 3 - if test -n "$__fish_git_prompt_show_informative_status" - set char $argv[3] - end - end + if test (count $argv) -ge 3 + if test -n "$__fish_git_prompt_show_informative_status" + set char $argv[3] + end + end - set -l variable _$user_variable_name - set -l variable_done "$variable"_done + set -l variable _$user_variable_name + set -l variable_done "$variable"_done - if not set -q $variable - set -g $variable (set -q $user_variable_name; and echo $user_variable; or echo $char) - end + if not set -q $variable + set -g $variable (set -q $user_variable_name; and echo $user_variable; or echo $char) + end end function __fish_git_prompt_validate_chars --description "__fish_git_prompt helper, checks char variables" - __fish_git_prompt_set_char __fish_git_prompt_char_cleanstate '✔' - __fish_git_prompt_set_char __fish_git_prompt_char_dirtystate '*' '✚' - __fish_git_prompt_set_char __fish_git_prompt_char_invalidstate '#' '✖' - __fish_git_prompt_set_char __fish_git_prompt_char_stagedstate '+' '●' - __fish_git_prompt_set_char __fish_git_prompt_char_stashstate '$' - __fish_git_prompt_set_char __fish_git_prompt_char_stateseparator ' ' '|' - __fish_git_prompt_set_char __fish_git_prompt_char_untrackedfiles '%' '…' - __fish_git_prompt_set_char __fish_git_prompt_char_upstream_ahead '>' '↑' - __fish_git_prompt_set_char __fish_git_prompt_char_upstream_behind '<' '↓' - __fish_git_prompt_set_char __fish_git_prompt_char_upstream_diverged '<>' - __fish_git_prompt_set_char __fish_git_prompt_char_upstream_equal '=' - __fish_git_prompt_set_char __fish_git_prompt_char_upstream_prefix '' + __fish_git_prompt_set_char __fish_git_prompt_char_cleanstate '✔' + __fish_git_prompt_set_char __fish_git_prompt_char_dirtystate '*' '✚' + __fish_git_prompt_set_char __fish_git_prompt_char_invalidstate '#' '✖' + __fish_git_prompt_set_char __fish_git_prompt_char_stagedstate '+' '●' + __fish_git_prompt_set_char __fish_git_prompt_char_stashstate '$' + __fish_git_prompt_set_char __fish_git_prompt_char_stateseparator ' ' '|' + __fish_git_prompt_set_char __fish_git_prompt_char_untrackedfiles '%' '…' + __fish_git_prompt_set_char __fish_git_prompt_char_upstream_ahead '>' '↑' + __fish_git_prompt_set_char __fish_git_prompt_char_upstream_behind '<' '↓' + __fish_git_prompt_set_char __fish_git_prompt_char_upstream_diverged '<>' + __fish_git_prompt_set_char __fish_git_prompt_char_upstream_equal '=' + __fish_git_prompt_set_char __fish_git_prompt_char_upstream_prefix '' end function __fish_git_prompt_set_color - set -l user_variable_name "$argv[1]" - set -l user_variable $$user_variable_name - set -l user_variable_bright + set -l user_variable_name "$argv[1]" + set -l user_variable $$user_variable_name + set -l user_variable_bright - set -l default default_done - switch (count $argv) - case 1 # No defaults given, use prompt color - set default $___fish_git_prompt_color - set default_done $___fish_git_prompt_color_done - case 2 # One default given, use normal for done - set default "$argv[2]" - set default_done (set_color normal) - case 3 # Both defaults given - set default "$argv[2]" - set default_done "$argv[3]" - end + set -l default default_done + switch (count $argv) + case 1 # No defaults given, use prompt color + set default $___fish_git_prompt_color + set default_done $___fish_git_prompt_color_done + case 2 # One default given, use normal for done + set default "$argv[2]" + set default_done (set_color normal) + case 3 # Both defaults given + set default "$argv[2]" + set default_done "$argv[3]" + end - set -l variable _$user_variable_name - set -l variable_done "$variable"_done + set -l variable _$user_variable_name + set -l variable_done "$variable"_done - if not set -q $variable - if test -n "$user_variable" - set -g $variable (set_color $user_variable) - set -g $variable_done (set_color normal) - else - set -g $variable $default - set -g $variable_done $default_done - end - end + if not set -q $variable + if test -n "$user_variable" + set -g $variable (set_color $user_variable) + set -g $variable_done (set_color normal) + else + set -g $variable $default + set -g $variable_done $default_done + end + end end function __fish_git_prompt_validate_colors --description "__fish_git_prompt helper, checks color variables" - # Base color defaults to nothing (must be done first) - __fish_git_prompt_set_color __fish_git_prompt_color '' '' + # Base color defaults to nothing (must be done first) + __fish_git_prompt_set_color __fish_git_prompt_color '' '' - # Normal colors - __fish_git_prompt_set_color __fish_git_prompt_color_prefix - __fish_git_prompt_set_color __fish_git_prompt_color_suffix - __fish_git_prompt_set_color __fish_git_prompt_color_bare - __fish_git_prompt_set_color __fish_git_prompt_color_merging - __fish_git_prompt_set_color __fish_git_prompt_color_cleanstate - __fish_git_prompt_set_color __fish_git_prompt_color_invalidstate - __fish_git_prompt_set_color __fish_git_prompt_color_upstream + # Normal colors + __fish_git_prompt_set_color __fish_git_prompt_color_prefix + __fish_git_prompt_set_color __fish_git_prompt_color_suffix + __fish_git_prompt_set_color __fish_git_prompt_color_bare + __fish_git_prompt_set_color __fish_git_prompt_color_merging + __fish_git_prompt_set_color __fish_git_prompt_color_cleanstate + __fish_git_prompt_set_color __fish_git_prompt_color_invalidstate + __fish_git_prompt_set_color __fish_git_prompt_color_upstream - # Colors with defaults with showcolorhints - if test -n "$__fish_git_prompt_showcolorhints" - __fish_git_prompt_set_color __fish_git_prompt_color_flags (set_color --bold blue) - __fish_git_prompt_set_color __fish_git_prompt_color_branch (set_color green) - __fish_git_prompt_set_color __fish_git_prompt_color_dirtystate (set_color red) - __fish_git_prompt_set_color __fish_git_prompt_color_stagedstate (set_color green) - else - __fish_git_prompt_set_color __fish_git_prompt_color_flags - __fish_git_prompt_set_color __fish_git_prompt_color_branch - __fish_git_prompt_set_color __fish_git_prompt_color_dirtystate $___fish_git_prompt_color_flags $___fish_git_prompt_color_flags_done - __fish_git_prompt_set_color __fish_git_prompt_color_stagedstate $___fish_git_prompt_color_flags $___fish_git_prompt_color_flags_done - end + # Colors with defaults with showcolorhints + if test -n "$__fish_git_prompt_showcolorhints" + __fish_git_prompt_set_color __fish_git_prompt_color_flags (set_color --bold blue) + __fish_git_prompt_set_color __fish_git_prompt_color_branch (set_color green) + __fish_git_prompt_set_color __fish_git_prompt_color_dirtystate (set_color red) + __fish_git_prompt_set_color __fish_git_prompt_color_stagedstate (set_color green) + else + __fish_git_prompt_set_color __fish_git_prompt_color_flags + __fish_git_prompt_set_color __fish_git_prompt_color_branch + __fish_git_prompt_set_color __fish_git_prompt_color_dirtystate $___fish_git_prompt_color_flags $___fish_git_prompt_color_flags_done + __fish_git_prompt_set_color __fish_git_prompt_color_stagedstate $___fish_git_prompt_color_flags $___fish_git_prompt_color_flags_done + end - # Branch_detached has a default, but is only used with showcolorhints - __fish_git_prompt_set_color __fish_git_prompt_color_branch_detached (set_color red) + # Branch_detached has a default, but is only used with showcolorhints + __fish_git_prompt_set_color __fish_git_prompt_color_branch_detached (set_color red) - # Colors that depend on flags color - __fish_git_prompt_set_color __fish_git_prompt_color_stashstate $___fish_git_prompt_color_flags $___fish_git_prompt_color_flags_done - __fish_git_prompt_set_color __fish_git_prompt_color_untrackedfiles $___fish_git_prompt_color_flags $___fish_git_prompt_color_flags_done + # Colors that depend on flags color + __fish_git_prompt_set_color __fish_git_prompt_color_stashstate $___fish_git_prompt_color_flags $___fish_git_prompt_color_flags_done + __fish_git_prompt_set_color __fish_git_prompt_color_untrackedfiles $___fish_git_prompt_color_flags $___fish_git_prompt_color_flags_done end set -l varargs for var in repaint describe_style show_informative_status showdirtystate showstashstate showuntrackedfiles showupstream - set varargs $varargs --on-variable __fish_git_prompt_$var + set varargs $varargs --on-variable __fish_git_prompt_$var end function __fish_git_prompt_repaint $varargs --description "Event handler, repaints prompt when functionality changes" - if status --is-interactive - if test $argv[3] = __fish_git_prompt_show_informative_status - # Clear characters that have different defaults with/without informative status - for name in cleanstate dirtystate invalidstate stagedstate stateseparator untrackedfiles upstream_ahead upstream_behind - set -e ___fish_git_prompt_char_$name - end - end + if status --is-interactive + if test $argv[3] = __fish_git_prompt_show_informative_status + # Clear characters that have different defaults with/without informative status + for name in cleanstate dirtystate invalidstate stagedstate stateseparator untrackedfiles upstream_ahead upstream_behind + set -e ___fish_git_prompt_char_$name + end + end - commandline -f repaint ^/dev/null - end + commandline -f repaint ^/dev/null + end end set -l varargs for var in '' _prefix _suffix _bare _merging _cleanstate _invalidstate _upstream _flags _branch _dirtystate _stagedstate _branch_detached _stashstate _untrackedfiles - set varargs $varargs --on-variable __fish_git_prompt_color$var + set varargs $varargs --on-variable __fish_git_prompt_color$var end set varargs $varargs --on-variable __fish_git_prompt_showcolorhints function __fish_git_prompt_repaint_color $varargs --description "Event handler, repaints prompt when any color changes" - if status --is-interactive - set -l var $argv[3] - set -e _$var - set -e _{$var}_done - if test $var = __fish_git_prompt_color -o $var = __fish_git_prompt_color_flags -o $var = __fish_git_prompt_showcolorhints - # reset all the other colors too - for name in prefix suffix bare merging branch dirtystate stagedstate invalidstate stashstate untrackedfiles upstream flags - set -e ___fish_git_prompt_color_$name - set -e ___fish_git_prompt_color_{$name}_done - end - end - commandline -f repaint ^/dev/null - end + if status --is-interactive + set -l var $argv[3] + set -e _$var + set -e _{$var}_done + if test $var = __fish_git_prompt_color -o $var = __fish_git_prompt_color_flags -o $var = __fish_git_prompt_showcolorhints + # reset all the other colors too + for name in prefix suffix bare merging branch dirtystate stagedstate invalidstate stashstate untrackedfiles upstream flags + set -e ___fish_git_prompt_color_$name + set -e ___fish_git_prompt_color_{$name}_done + end + end + commandline -f repaint ^/dev/null + end end set -l varargs for var in cleanstate dirtystate invalidstate stagedstate stashstate stateseparator untrackedfiles upstream_ahead upstream_behind upstream_diverged upstream_equal upstream_prefix - set varargs $varargs --on-variable __fish_git_prompt_char_$var + set varargs $varargs --on-variable __fish_git_prompt_char_$var end function __fish_git_prompt_repaint_char $varargs --description "Event handler, repaints prompt when any char changes" - if status --is-interactive - set -e _$argv[3] - commandline -f repaint ^/dev/null - end + if status --is-interactive + set -e _$argv[3] + commandline -f repaint ^/dev/null + end end From ebde55f7047aa56ce69bf33ce8fb62c7e9f2a37d Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 29 May 2016 14:31:12 +0200 Subject: [PATCH 336/363] Man completions: Show all pages for a section If one is given, of course. --- share/functions/__fish_complete_man.fish | 35 +++++++++++++++--------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/share/functions/__fish_complete_man.fish b/share/functions/__fish_complete_man.fish index 1bdaa5c6a..7306dd2cb 100644 --- a/share/functions/__fish_complete_man.fish +++ b/share/functions/__fish_complete_man.fish @@ -1,27 +1,33 @@ function __fish_complete_man - if test (commandline -ct) + # Try to guess what section to search in. If we don't know, we + # use [^)]*, which should match any section - # Try to guess what section to search in. If we don't know, we - # use [^)]*, which should match any section - - set section "" - set prev (commandline -poc) - set -e prev[1] - while count $prev - switch $prev[1] + set section "" + set prev (commandline -poc) + set -e prev[1] + while count $prev + switch $prev[1] case '-**' case '*' set section $prev[1] - end - set -e prev[1] end + set -e prev[1] + end - set section $section"[^)]*" + set section $section"[^)]*" + set -l token (commandline -ct) + # If we don't have a token but a section, list all pages for that section. + # Don't do it for all sections because that would be overwhelming. + if test -z "$token" -a "$section" != "[^)]*" + set token "." + end + + if test -n "$token" # Do the actual search - apropos (commandline -ct) ^/dev/null | awk ' + apropos $token ^/dev/null | awk ' BEGIN { FS="[\t ]- "; OFS="\t"; } # BSD/Darwin /^[^( \t]+\('$section'\)/ { @@ -58,6 +64,9 @@ function __fish_complete_man print name, sect } ' + else + return 1 end + return 0 end From 0d257fd651ce244a25d1f262f8dafdc99bcbad44 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 29 May 2016 14:31:42 +0200 Subject: [PATCH 337/363] Man completions: Don't show sections when completing pages --- share/completions/man.fish | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/share/completions/man.fish b/share/completions/man.fish index 38e5ca4dd..c70a1ab02 100644 --- a/share/completions/man.fish +++ b/share/completions/man.fish @@ -1,20 +1,21 @@ complete -xc man -a "(__fish_complete_man)" -complete -xc man -a 1 --description "Program section" -complete -xc man -a 2 --description "Syscall section" -complete -xc man -a 3 --description "Library section" -complete -xc man -a 4 --description "Device section" -complete -xc man -a 5 --description "File format section" -complete -xc man -a 6 --description "Games section" -complete -xc man -a 7 --description "Misc section" -complete -xc man -a 8 --description "Admin section" -complete -xc man -a 9 --description "Kernel section" -complete -xc man -a tcl --description "Tcl section" -complete -xc man -a n --description "New section" -complete -xc man -a l --description "Local section" -complete -xc man -a p -complete -xc man -a o --description "Old section" +complete -xc man -n 'not __fish_complete_man' -a 1 -d 'Program section' +complete -xc man -n 'not __fish_complete_man' -a 2 -d 'Syscall section' +complete -xc man -n 'not __fish_complete_man' -a 3 -d 'Library section' +complete -xc man -n 'not __fish_complete_man' -a 4 -d 'Device section' +complete -xc man -n 'not __fish_complete_man' -a 5 -d 'File format section' +complete -xc man -n 'not __fish_complete_man' -a 6 -d 'Games section' +complete -xc man -n 'not __fish_complete_man' -a 7 -d 'Misc section' +complete -xc man -n 'not __fish_complete_man' -a 8 -d 'Admin section' +complete -xc man -n 'not __fish_complete_man' -a 9 -d 'Kernel section' +complete -xc man -n 'not __fish_complete_man' -a tcl -d 'Tcl section' +complete -xc man -n 'not __fish_complete_man' -a n -d 'New section' +complete -xc man -n 'not __fish_complete_man' -a l -d 'Local section' +complete -xc man -n 'not __fish_complete_man' -a p +complete -xc man -n 'not __fish_complete_man' -a o -d 'Old section' + complete -rc man -s C --description "Configuration file" complete -xc man -s M -a "(__fish_complete_directories (commandline -ct))" --description "Manpath" complete -rc man -s P --description "Pager" From bfb5fec330b24fa54f648fdc96e2b2e40639eaef Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 29 May 2016 14:34:20 +0200 Subject: [PATCH 338/363] Remove stray "0" output from man completions --- share/functions/__fish_complete_man.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/functions/__fish_complete_man.fish b/share/functions/__fish_complete_man.fish index 7306dd2cb..bd9818bb9 100644 --- a/share/functions/__fish_complete_man.fish +++ b/share/functions/__fish_complete_man.fish @@ -6,7 +6,7 @@ function __fish_complete_man set section "" set prev (commandline -poc) set -e prev[1] - while count $prev + while set -q prev[1] switch $prev[1] case '-**' From 3d19b549c8d9fc77597d0de95260e90f5dda2dae Mon Sep 17 00:00:00 2001 From: Andreas Wagner Date: Sun, 29 May 2016 16:13:13 -0400 Subject: [PATCH 339/363] Fix utf-8 decoding error in file_is_overwritable of create_manpage_completions.py --- share/tools/create_manpage_completions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/tools/create_manpage_completions.py b/share/tools/create_manpage_completions.py index bd15eaebf..ea7a66085 100755 --- a/share/tools/create_manpage_completions.py +++ b/share/tools/create_manpage_completions.py @@ -664,7 +664,7 @@ class TypeDeroffManParser(ManParser): # Raises IOError if it cannot be opened def file_is_overwritable(path): result = False - file = open(path, 'r') + file = codecs.open(path, "r", encoding="utf-8") for line in file: # Skip leading empty lines line = line.strip() From 5bf1b0e5f500b6b99a866da32dd9002219cac6d6 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sat, 28 May 2016 22:28:26 -0700 Subject: [PATCH 340/363] fix random lint issues This only eliminates errors reported by `make lint`. It shouldn't cause any functional changes. This change does remove several functions that are unused. It also removes the `desc_arr` variable which is both unused and out of date with reality. --- build_tools/iwyu.linux.imp | 9 ++++++ configure.ac | 2 +- src/autoload.cpp | 2 +- src/builtin.cpp | 6 ++-- src/builtin_commandline.cpp | 6 ++-- src/complete.cpp | 15 +++++---- src/env.cpp | 4 +-- src/env_universal_common.cpp | 5 +-- src/expand.cpp | 11 ------- src/expand.h | 3 -- src/fallback.cpp | 50 ++-------------------------- src/fallback.h | 13 +------- src/fish_tests.cpp | 63 +----------------------------------- src/function.cpp | 26 +++++++-------- src/highlight.cpp | 1 + src/history.cpp | 21 +++--------- src/input.cpp | 33 ------------------- src/intern.cpp | 4 +-- src/iothread.cpp | 15 +++++---- src/kill.cpp | 3 -- src/output.cpp | 6 ++-- src/parse_tree.cpp | 10 ++++-- src/parser.cpp | 11 +++---- src/parser.h | 6 ++-- src/proc.cpp | 60 ++++++++++------------------------ src/signal.cpp | 2 ++ src/util.cpp | 1 - src/wutil.cpp | 2 +- 28 files changed, 102 insertions(+), 288 deletions(-) diff --git a/build_tools/iwyu.linux.imp b/build_tools/iwyu.linux.imp index 27700b676..9152cfb9d 100644 --- a/build_tools/iwyu.linux.imp +++ b/build_tools/iwyu.linux.imp @@ -13,7 +13,16 @@ { symbol: ["size_t", "private", "", "public"] }, { symbol: ["size_t", "private", "", "public"] }, { symbol: ["size_t", "private", "", "public"] }, + { symbol: ["intmax_t", "private", "", "public"] }, + { symbol: ["intmax_t", "private", "", "public"] }, + { symbol: ["uint32_t", "private", "", "public"] }, + { symbol: ["uint32_t", "private", "", "public"] }, + { symbol: ["uint64_t", "private", "", "public"] }, { symbol: ["uint64_t", "private", "", "public"] }, + { symbol: ["uintmax_t", "private", "", "public"] }, + { symbol: ["uintmax_t", "private", "", "public"] }, + { symbol: ["clock_gettime", "private", "", "public"] }, + { symbol: ["timespec", "private", "", "public"] }, { symbol: ["memset", "private", "", "public"] }, { symbol: ["strerror", "private", "", "public"] }, ] diff --git a/configure.ac b/configure.ac index 8b0ea8780..dae16589a 100644 --- a/configure.ac +++ b/configure.ac @@ -310,7 +310,7 @@ AC_STRUCT_DIRENT_D_TYPE AC_CHECK_FUNCS( wcsndup ) AC_CHECK_FUNCS( futimes ) -AC_CHECK_FUNCS( wcslcat wcslcpy lrand48_r killpg ) +AC_CHECK_FUNCS( wcslcpy lrand48_r killpg ) AC_CHECK_FUNCS( backtrace_symbols getifaddrs ) AC_CHECK_FUNCS( futimens clock_gettime ) diff --git a/src/autoload.cpp b/src/autoload.cpp index 2d404cf9d..9a8ebf408 100644 --- a/src/autoload.cpp +++ b/src/autoload.cpp @@ -86,7 +86,7 @@ int autoload_t::load(const wcstring &cmd, bool reload) { this->last_path_tokenized.clear(); tokenize_variable_array(this->last_path, this->last_path_tokenized); - scoped_lock locker(lock); + scoped_lock locker(lock); //!OCLINT(side-effect) this->evict_all_nodes(); } diff --git a/src/builtin.cpp b/src/builtin.cpp index 336f09eb5..b445cdf03 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -81,7 +81,7 @@ // The send stuff to foreground message. #define FG_MSG _(L"Send job %d, '%ls' to foreground\n") -/// Datastructure to describe a builtin. +/// Data structure to describe a builtin. struct builtin_data_t { // Name of the builtin. const wchar_t *name; @@ -1807,9 +1807,7 @@ static int builtin_random(parser_t &parser, io_streams_t &streams, wchar_t **arg srand48_r(time(0), &seed_buffer); } lrand48_r(&seed_buffer, &res); - // The labs() shouldn't be necessary since lrand48 is supposed to - // return only positive integers but we're going to play it safe. - streams.out.append_format(L"%ld\n", labs(res % 32768)); + streams.out.append_format(L"%ld\n", res % 32768); break; } case 1: { diff --git a/src/builtin_commandline.cpp b/src/builtin_commandline.cpp index c25e60411..0a76ef845 100644 --- a/src/builtin_commandline.cpp +++ b/src/builtin_commandline.cpp @@ -67,7 +67,7 @@ static wcstring_list_t *get_transient_stack() { static bool get_top_transient(wcstring *out_result) { ASSERT_IS_MAIN_THREAD(); bool result = false; - scoped_lock locker(transient_commandline_lock); + scoped_lock locker(transient_commandline_lock); //!OCLINT(side-effect) const wcstring_list_t *stack = get_transient_stack(); if (!stack->empty()) { out_result->assign(stack->back()); @@ -79,7 +79,7 @@ static bool get_top_transient(wcstring *out_result) { builtin_commandline_scoped_transient_t::builtin_commandline_scoped_transient_t( const wcstring &cmd) { ASSERT_IS_MAIN_THREAD(); - scoped_lock locker(transient_commandline_lock); + scoped_lock locker(transient_commandline_lock); //!OCLINT(side-effect) wcstring_list_t *stack = get_transient_stack(); stack->push_back(cmd); this->token = stack->size(); @@ -87,7 +87,7 @@ builtin_commandline_scoped_transient_t::builtin_commandline_scoped_transient_t( builtin_commandline_scoped_transient_t::~builtin_commandline_scoped_transient_t() { ASSERT_IS_MAIN_THREAD(); - scoped_lock locker(transient_commandline_lock); + scoped_lock locker(transient_commandline_lock); //!OCLINT(side-effect) wcstring_list_t *stack = get_transient_stack(); assert(this->token == stack->size()); stack->pop_back(); diff --git a/src/complete.cpp b/src/complete.cpp index 59da5a94e..6062a055b 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -425,7 +425,7 @@ static completion_entry_t &complete_get_exact_entry(const wcstring &cmd, bool cm void complete_set_authoritative(const wchar_t *cmd, bool cmd_is_path, bool authoritative) { CHECK(cmd, ); - scoped_lock lock(completion_lock); + scoped_lock lock(completion_lock); //!OCLINT(has side effects) completion_entry_t &c = complete_get_exact_entry(cmd, cmd_is_path); c.authoritative = authoritative; @@ -439,7 +439,7 @@ void complete_add(const wchar_t *cmd, bool cmd_is_path, const wcstring &option, assert(option.empty() == (option_type == option_type_args_only)); // Lock the lock that allows us to edit the completion entry list. - scoped_lock lock(completion_lock); + scoped_lock lock(completion_lock); //!OCLINT(has side effects) completion_entry_t &c = complete_get_exact_entry(cmd, cmd_is_path); @@ -476,7 +476,7 @@ bool completion_entry_t::remove_option(const wcstring &option, complete_option_t void complete_remove(const wcstring &cmd, bool cmd_is_path, const wcstring &option, complete_option_type_t type) { - scoped_lock lock(completion_lock); + scoped_lock lock(completion_lock); //!OCLINT(has side effects) completion_entry_t tmp_entry(cmd, cmd_is_path, false); completion_entry_set_t::iterator iter = completion_set.find(tmp_entry); @@ -493,7 +493,7 @@ void complete_remove(const wcstring &cmd, bool cmd_is_path, const wcstring &opti } void complete_remove_all(const wcstring &cmd, bool cmd_is_path) { - scoped_lock lock(completion_lock); + scoped_lock lock(completion_lock); //!OCLINT(has side effects) completion_entry_t tmp_entry(cmd, cmd_is_path, false); completion_set.erase(tmp_entry); @@ -1340,6 +1340,7 @@ void complete(const wcstring &cmd_with_subcmds, std::vector *out_c } } + // cppcheck-suppress nullPointerRedundantCheck if (cmd_node && cmd_node->location_in_or_at_end_of_source_range(pos)) { // Complete command filename. completer.complete_cmd(current_token, use_function, use_builtin, use_command, @@ -1478,7 +1479,7 @@ static void append_switch(wcstring &out, const wcstring &opt, const wcstring &ar wcstring complete_print() { wcstring out; - scoped_lock locker(completion_lock); + scoped_lock locker(completion_lock); //!OCLINT(side-effect) // Get a list of all completions in a vector, then sort it by order. std::vector all_completions; @@ -1595,7 +1596,7 @@ wcstring_list_t complete_get_wrap_chain(const wcstring &command) { if (command.empty()) { return wcstring_list_t(); } - scoped_lock locker(wrapper_lock); + scoped_lock locker(wrapper_lock); //!OCLINT(side-effect) const wrapper_map_t &wraps = wrap_map(); wcstring_list_t result; @@ -1631,7 +1632,7 @@ wcstring_list_t complete_get_wrap_chain(const wcstring &command) { wcstring_list_t complete_get_wrap_pairs() { wcstring_list_t result; - scoped_lock locker(wrapper_lock); + scoped_lock locker(wrapper_lock); //!OCLINT(side-effect) const wrapper_map_t &wraps = wrap_map(); for (wrapper_map_t::const_iterator outer = wraps.begin(); outer != wraps.end(); ++outer) { const wcstring &cmd = outer->first; diff --git a/src/env.cpp b/src/env.cpp index 381b45886..e09064ad7 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -699,7 +699,7 @@ env_var_t env_get_string(const wcstring &key, env_mode_flags_t mode) { if (search_local || search_global) { /* Lock around a local region */ - scoped_lock lock(env_lock); + scoped_lock lock(env_lock); //!OCLINT(has side effects) env_node_t *env = search_local ? top : global_env; @@ -869,7 +869,7 @@ static void add_key_to_string_set(const var_table_t &envs, std::set *s } wcstring_list_t env_get_names(int flags) { - scoped_lock lock(env_lock); + scoped_lock lock(env_lock); //!OCLINT(has side effects) wcstring_list_t result; std::set names; diff --git a/src/env_universal_common.cpp b/src/env_universal_common.cpp index bb5b759be..c1b7c46cd 100644 --- a/src/env_universal_common.cpp +++ b/src/env_universal_common.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -311,7 +312,7 @@ void env_universal_t::set_internal(const wcstring &key, const wcstring &val, boo } void env_universal_t::set(const wcstring &key, const wcstring &val, bool exportv) { - scoped_lock locker(lock); + scoped_lock locker(lock); //!OCLINT(side-effect) this->set_internal(key, val, exportv, true /* overwrite */); } @@ -331,7 +332,7 @@ bool env_universal_t::remove(const wcstring &key) { wcstring_list_t env_universal_t::get_names(bool show_exported, bool show_unexported) const { wcstring_list_t result; - scoped_lock locker(lock); + scoped_lock locker(lock); //!OCLINT(side-effect) var_table_t::const_iterator iter; for (iter = vars.begin(); iter != vars.end(); ++iter) { const wcstring &key = iter->first; diff --git a/src/expand.cpp b/src/expand.cpp index b69c79f82..5ea21a144 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -433,17 +433,6 @@ bool process_iterator_t::next_process(wcstring *out_str, pid_t *out_pid) { #endif -std::vector expand_get_all_process_names(void) { - wcstring name; - pid_t pid; - process_iterator_t iterator; - std::vector result; - while (iterator.next_process(&name, &pid)) { - result.push_back(name); - } - return result; -} - // Helper function to do a job search. struct find_job_data_t { const wchar_t *proc; // the process to search for - possibly numeric, possibly a name diff --git a/src/expand.h b/src/expand.h index a0efd1fd9..7c82d72e7 100644 --- a/src/expand.h +++ b/src/expand.h @@ -144,9 +144,6 @@ void expand_tilde(wcstring &input); /// Perform the opposite of tilde expansion on the string, which is modified in place. wcstring replace_home_directory_with_tilde(const wcstring &str); -/// Testing function for getting all process names. -std::vector expand_get_all_process_names(void); - /// Abbreviation support. Expand src as an abbreviation, returning true if one was found, false if /// not. If result is not-null, returns the abbreviation by reference. #define USER_ABBREVIATIONS_VARIABLE_NAME L"fish_user_abbreviations" diff --git a/src/fallback.cpp b/src/fallback.cpp index b14b92434..9b7468425 100644 --- a/src/fallback.cpp +++ b/src/fallback.cpp @@ -90,6 +90,7 @@ char *tparm_solaris_kludge(char *str, ...) { #endif +#if __APPLE__ /// Fallback implementations of wcsdup and wcscasecmp. On systems where these are not needed (e.g. /// building on Linux) these should end up just being stripped, as they are static functions that /// are not referenced in this file. @@ -103,6 +104,7 @@ __attribute__((unused)) static wchar_t *wcsdup_fallback(const wchar_t *in) { memcpy(out, in, sizeof(wchar_t) * (len + 1)); return out; } +#endif __attribute__((unused)) static int wcscasecmp_fallback(const wchar_t *a, const wchar_t *b) { if (*a == 0) { @@ -168,54 +170,6 @@ wchar_t *wcsndup(const wchar_t *in, size_t c) { } #endif -#ifndef HAVE_WCSLCAT - -/*$OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $*/ - -/* - * Copyright (c) 1998 Todd C. Miller - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -size_t wcslcat(wchar_t *dst, const wchar_t *src, size_t siz) { - register wchar_t *d = dst; - register const wchar_t *s = src; - register size_t n = siz; - size_t dlen; - - // Find the end of dst and adjust bytes left but don't go past end. - while (n-- != 0 && *d != '\0') d++; - - dlen = d - dst; - n = siz - dlen; - - if (n == 0) return dlen + wcslen(s); - - while (*s != '\0') { - if (n != 1) { - *d++ = *s; - n--; - } - s++; - } - *d = '\0'; - - return dlen + (s - src); - /* count does not include NUL */ -} - -#endif #ifndef HAVE_WCSLCPY /*$OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $*/ diff --git a/src/fallback.h b/src/fallback.h index f167ff69f..09e301429 100644 --- a/src/fallback.h +++ b/src/fallback.h @@ -7,7 +7,6 @@ // compiling several modules that include this header because they use symbols which are defined as // macros in . // IWYU pragma: no_include -#include #include #include // The following include must be kept despite what IWYU says. That's because of the interaction @@ -83,16 +82,6 @@ wchar_t *wcsndup(const wchar_t *in, size_t c); wchar_t *wcsndup(const wchar_t *in, size_t c); #endif -#ifndef HAVE_WCSLCAT -/// Appends src to string dst of size siz (unlike wcsncat, siz is the full size of dst, not space -/// left). At most siz-1 characters will be copied. Always NUL terminates (unless siz <= -/// wcslen(dst)). Returns wcslen(src) + MIN(siz, wcslen(initial dst)). If retval >= siz, -/// truncation occurred. -/// -/// This is the OpenBSD strlcat function, modified for wide characters, and renamed to reflect this -/// change. -size_t wcslcat(wchar_t *dst, const wchar_t *src, size_t siz); -#endif #ifndef HAVE_WCSLCPY /// Copy src to string dst of size siz. At most siz-1 characters will be copied. Always NUL /// terminates (unless siz == 0). Returns wcslen(src); if retval >= siz, truncation occurred. @@ -103,7 +92,7 @@ size_t wcslcpy(wchar_t *dst, const wchar_t *src, size_t siz); #endif #ifndef HAVE_LRAND48_R -/// Datastructure for the lrand48_r fallback implementation. +/// Data structure for the lrand48_r fallback implementation. struct drand48_data { unsigned int seed; }; diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 49c9327bb..29040e227 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -60,7 +61,6 @@ #include "signal.h" #include "tokenizer.h" #include "utf8.h" -#include "util.h" #include "wcstringutil.h" #include "wildcard.h" #include "wutil.h" // IWYU pragma: keep @@ -2194,62 +2194,6 @@ static void test_autosuggestion_combining() { do_test(combine_command_and_autosuggestion(L"alpha", L"ALPHA") == L"alpha"); } -/// Test speed of completion calculations. -void perf_complete() { - wchar_t c; - std::vector out; - long long t1, t2; - int matches = 0; - double t; - wchar_t str[3] = {0, 0, 0}; - int i; - - say(L"Testing completion performance"); - - reader_push(L""); - say(L"Here we go"); - - t1 = get_time(); - - for (c = L'a'; c <= L'z'; c++) { - str[0] = c; - reader_set_buffer(str, 0); - - complete(str, &out, COMPLETION_REQUEST_DEFAULT, env_vars_snapshot_t::current()); - - matches += out.size(); - out.clear(); - } - t2 = get_time(); - - t = (double)(t2 - t1) / (1000000 * 26); - - say(L"One letter command completion took %f seconds per completion, %f microseconds/match", t, - (double)(t2 - t1) / matches); - - matches = 0; - t1 = get_time(); - for (i = 0; i < LAPS; i++) { - str[0] = 'a' + (rand() % 26); - str[1] = 'a' + (rand() % 26); - - reader_set_buffer(str, 0); - - complete(str, &out, COMPLETION_REQUEST_DEFAULT, env_vars_snapshot_t::current()); - - matches += out.size(); - out.clear(); - } - t2 = get_time(); - - t = (double)(t2 - t1) / (1000000 * LAPS); - - say(L"Two letter command completion took %f seconds per completion, %f microseconds/match", t, - (double)(t2 - t1) / matches); - - reader_pop(); -} - static void test_history_matches(history_search_t &search, size_t matches) { size_t i; for (i = 0; i < matches; i++) { @@ -3937,11 +3881,6 @@ int main(int argc, char **argv) { say(L"Encountered %d errors in low-level tests", err_count); if (s_test_run_count == 0) say(L"*** No Tests Were Actually Run! ***"); - // Skip performance tests for now, since they seem to hang when running from inside make. - - // say( L"Testing performance" ); - // perf_complete(); - reader_destroy(); builtin_destroy(); event_destroy(); diff --git a/src/function.cpp b/src/function.cpp index fc3974722..21f7d9635 100644 --- a/src/function.cpp +++ b/src/function.cpp @@ -63,7 +63,7 @@ static bool is_autoload = false; /// loaded. static int load(const wcstring &name) { ASSERT_IS_MAIN_THREAD(); - scoped_lock lock(functions_lock); + scoped_lock lock(functions_lock); //!OCLINT(has side effects) bool was_autoload = is_autoload; int res; @@ -163,7 +163,7 @@ void function_add(const function_data_t &data, const parser_t &parser, int defin CHECK(!data.name.empty(), ); CHECK(data.definition, ); - scoped_lock lock(functions_lock); + scoped_lock lock(functions_lock); //!OCLINT(has side effects) // Remove the old function. function_remove(data.name); @@ -184,21 +184,21 @@ void function_add(const function_data_t &data, const parser_t &parser, int defin int function_exists(const wcstring &cmd) { if (parser_keywords_is_reserved(cmd)) return 0; - scoped_lock lock(functions_lock); + scoped_lock lock(functions_lock); //!OCLINT(has side effects) load(cmd); return loaded_functions.find(cmd) != loaded_functions.end(); } void function_load(const wcstring &cmd) { if (!parser_keywords_is_reserved(cmd)) { - scoped_lock lock(functions_lock); + scoped_lock lock(functions_lock); //!OCLINT(has side effects) load(cmd); } } int function_exists_no_autoload(const wcstring &cmd, const env_vars_snapshot_t &vars) { if (parser_keywords_is_reserved(cmd)) return 0; - scoped_lock lock(functions_lock); + scoped_lock lock(functions_lock); //!OCLINT(has side effects) return loaded_functions.find(cmd) != loaded_functions.end() || function_autoloader.can_load(cmd, vars); } @@ -248,25 +248,25 @@ bool function_get_definition(const wcstring &name, wcstring *out_definition) { } wcstring_list_t function_get_named_arguments(const wcstring &name) { - scoped_lock lock(functions_lock); + scoped_lock lock(functions_lock); //!OCLINT(has side effects) const function_info_t *func = function_get(name); return func ? func->named_arguments : wcstring_list_t(); } std::map function_get_inherit_vars(const wcstring &name) { - scoped_lock lock(functions_lock); + scoped_lock lock(functions_lock); //!OCLINT(has side effects) const function_info_t *func = function_get(name); return func ? func->inherit_vars : std::map(); } int function_get_shadow_builtin(const wcstring &name) { - scoped_lock lock(functions_lock); + scoped_lock lock(functions_lock); //!OCLINT(has side effects) const function_info_t *func = function_get(name); return func ? func->shadow_builtin : false; } int function_get_shadow_scope(const wcstring &name) { - scoped_lock lock(functions_lock); + scoped_lock lock(functions_lock); //!OCLINT(has side effects) const function_info_t *func = function_get(name); return func ? func->shadow_scope : false; } @@ -285,7 +285,7 @@ bool function_get_desc(const wcstring &name, wcstring *out_desc) { void function_set_desc(const wcstring &name, const wcstring &desc) { load(name); - scoped_lock lock(functions_lock); + scoped_lock lock(functions_lock); //!OCLINT(has side effects) function_map_t::iterator iter = loaded_functions.find(name); if (iter != loaded_functions.end()) { iter->second.description = desc; @@ -309,7 +309,7 @@ bool function_copy(const wcstring &name, const wcstring &new_name) { wcstring_list_t function_get_names(int get_hidden) { std::set names; - scoped_lock lock(functions_lock); + scoped_lock lock(functions_lock); //!OCLINT(has side effects) autoload_names(names, get_hidden); function_map_t::const_iterator iter; @@ -326,13 +326,13 @@ wcstring_list_t function_get_names(int get_hidden) { } const wchar_t *function_get_definition_file(const wcstring &name) { - scoped_lock lock(functions_lock); + scoped_lock lock(functions_lock); //!OCLINT(has side effects) const function_info_t *func = function_get(name); return func ? func->definition_file : NULL; } int function_get_definition_offset(const wcstring &name) { - scoped_lock lock(functions_lock); + scoped_lock lock(functions_lock); //!OCLINT(has side effects) const function_info_t *func = function_get(name); return func ? func->definition_offset : -1; } diff --git a/src/highlight.cpp b/src/highlight.cpp index 9dd6b224e..49dd2f065 100644 --- a/src/highlight.cpp +++ b/src/highlight.cpp @@ -1039,6 +1039,7 @@ const highlighter_t::color_array_t &highlighter_t::highlight() { std::fill(this->color_array.begin(), this->color_array.end(), 0); #if 0 + // Disabled for the 2.2.0 release: https://github.com/fish-shell/fish-shell/issues/1809. const wcstring dump = parse_dump_tree(parse_tree, buff); fprintf(stderr, "%ls\n", dump.c_str()); #endif diff --git a/src/history.cpp b/src/history.cpp index e849fb872..49ed1b67d 100644 --- a/src/history.cpp +++ b/src/history.cpp @@ -661,22 +661,11 @@ static size_t offset_of_next_item_fish_1_x(const char *begin, size_t mmap_length 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; - } - case history_type_fish_1_x: { - result = offset_of_next_item_fish_1_x(begin, mmap_length, inout_cursor); - break; - } - case history_type_unknown: { - // Oh well. - result = (size_t)-1; - break; - } + size_t result = (size_t)-1; + if (mmap_type == history_type_fish_2_0) { + result = offset_of_next_item_fish_2_0(begin, mmap_length, inout_cursor, cutoff_timestamp); + } else if (mmap_type == history_type_fish_1_x) { + result = offset_of_next_item_fish_1_x(begin, mmap_length, inout_cursor); } return result; } diff --git a/src/input.cpp b/src/input.cpp index efe2f5946..5b599e85d 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -131,39 +131,6 @@ wcstring describe_char(wint_t c) { return format_string(L"%02x", c); } -/// Description of each supported input function. -static const wchar_t *desc_arr[] = { - L"Move to beginning of line", - L"Move to end of line", - L"Move forward one character", - L"Move backward one character", - L"Move forward one word", - L"Move backward one word", - L"Search backward through list of previous commands", - L"Search forward through list of previous commands", - L"Delete one character forward", - L"Delete one character backward", - L"Move contents from cursor to end of line to killring", - L"Paste contents of killring", - L"Rotate to previous killring entry", - L"Guess the rest of the next input token", - L"Move to first item of history", - L"Move to last item of history", - L"Clear current line", - L"Move contents from beginning of line to cursor to killring", - L"Move entire line to killring", - L"Move next word to killring", - L"Move previous word to killring", - L"Write out key bindings", - L"Clear entire screen", - L"Quit the running program", - L"Search backward through list of previous commands for matching token", - L"Search forward through list of previous commands for matching token", - L"Insert the pressed key", - L"Do nothing", - L"End of file", - L"Repeat command"}; - /// Internal code for each supported input function. static const wchar_t code_arr[] = {R_BEGINNING_OF_LINE, R_END_OF_LINE, diff --git a/src/intern.cpp b/src/intern.cpp index d92b142a8..9030ef8a7 100644 --- a/src/intern.cpp +++ b/src/intern.cpp @@ -38,8 +38,8 @@ static pthread_mutex_t intern_lock = PTHREAD_MUTEX_INITIALIZER; static const wchar_t *intern_with_dup(const wchar_t *in, bool dup) { if (!in) return NULL; - // debug( 0, L"intern %ls", in ); - scoped_lock lock(intern_lock); + debug(5, L"intern %ls", in); + scoped_lock lock(intern_lock); //!OCLINT(has side effects) const wchar_t *result; #if USE_SET diff --git a/src/iothread.cpp b/src/iothread.cpp index 2780cda51..be08e4515 100644 --- a/src/iothread.cpp +++ b/src/iothread.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -107,7 +108,7 @@ static SpawnRequest_t *dequeue_spawn_request(void) { } static void enqueue_thread_result(SpawnRequest_t *req) { - scoped_lock lock(s_result_queue_lock); + scoped_lock lock(s_result_queue_lock); //!OCLINT(has side effects) s_result_queue.push(req); } @@ -195,7 +196,7 @@ int iothread_perform_base(int (*handler)(void *), void (*completionCallback)(voi { // Lock around a local region. Note that we can only access s_active_thread_count under the // lock. - scoped_lock lock(s_spawn_queue_lock); + scoped_lock lock(s_spawn_queue_lock); //!OCLINT(has side effects) add_to_queue(req); if (s_active_thread_count < IO_MAX_THREADS) { s_active_thread_count++; @@ -293,7 +294,7 @@ static void iothread_service_main_thread_requests(void) { // Move the queue to a local variable. std::queue request_queue; { - scoped_lock queue_lock(s_main_thread_request_queue_lock); + scoped_lock queue_lock(s_main_thread_request_queue_lock); //!OCLINT(has side effects) std::swap(request_queue, s_main_thread_request_queue); } @@ -316,7 +317,7 @@ static void iothread_service_main_thread_requests(void) { // // Because the waiting thread performs step 1 under the lock, if we take the lock, we avoid // posting before the waiting thread is waiting. - scoped_lock broadcast_lock(s_main_thread_performer_lock); + scoped_lock broadcast_lock(s_main_thread_performer_lock); //!OCLINT(has side effects) VOMIT_ON_FAILURE(pthread_cond_broadcast(&s_main_thread_performer_condition)); } } @@ -326,7 +327,7 @@ static void iothread_service_result_queue() { // Move the queue to a local variable. std::queue result_queue; { - scoped_lock queue_lock(s_result_queue_lock); + scoped_lock queue_lock(s_result_queue_lock); //!OCLINT(has side effects) std::swap(result_queue, s_result_queue); } @@ -357,7 +358,7 @@ int iothread_perform_on_main_base(int (*handler)(void *), void *context) { // Append it. Do not delete the nested scope as it is crucial to the proper functioning of this // code by virtue of the lock management. { - scoped_lock queue_lock(s_main_thread_request_queue_lock); + scoped_lock queue_lock(s_main_thread_request_queue_lock); //!OCLINT(has side effects) s_main_thread_request_queue.push(&req); } @@ -366,7 +367,7 @@ int iothread_perform_on_main_base(int (*handler)(void *), void *context) { VOMIT_ON_FAILURE(!write_loop(s_write_pipe, &wakeup_byte, sizeof wakeup_byte)); // Wait on the condition, until we're done. - scoped_lock perform_lock(s_main_thread_performer_lock); + scoped_lock perform_lock(s_main_thread_performer_lock); //!OCLINT(has side effects) while (!req.done) { // It would be nice to support checking for cancellation here, but the clients need a // deterministic way to clean up to avoid leaks diff --git a/src/kill.cpp b/src/kill.cpp index 706b18156..40dc5ecef 100644 --- a/src/kill.cpp +++ b/src/kill.cpp @@ -4,7 +4,6 @@ // previous cuts. #include "config.h" // IWYU pragma: keep -#include #include #include #include @@ -13,8 +12,6 @@ #include "common.h" #include "fallback.h" // IWYU pragma: keep -#include "kill.h" -#include "path.h" /** Kill ring */ typedef std::list kill_list_t; diff --git a/src/output.cpp b/src/output.cpp index 78ea210bb..0d7c74f0b 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -31,7 +31,7 @@ static int writeb_internal(char c); /// The function used for output. -static int (*out)(char c) = &writeb_internal; +static int (*out)(char c) = writeb_internal; /// Name of terminal. static wcstring current_term; @@ -119,7 +119,7 @@ void write_color(rgb_color_t color, bool is_fg) { // Background: ^[48;2;;;m color24_t rgb = color.to_color24(); char buff[128]; - snprintf(buff, sizeof buff, "\x1b[%u;2;%u;%u;%um", is_fg ? 38 : 48, rgb.rgb[0], rgb.rgb[1], + snprintf(buff, sizeof buff, "\x1b[%d;2;%u;%u;%um", is_fg ? 38 : 48, rgb.rgb[0], rgb.rgb[1], rgb.rgb[2]); int (*writer)(char) = output_get_writer(); if (writer) { @@ -263,7 +263,7 @@ void set_color(rgb_color_t c, rgb_color_t c2) { } /// Default output method, simply calls write() on stdout. -static int writeb_internal(char c) { +static int writeb_internal(char c) { // cppcheck write_loop(1, &c, 1); return 0; } diff --git a/src/parse_tree.cpp b/src/parse_tree.cpp index befa104b9..e5666f834 100644 --- a/src/parse_tree.cpp +++ b/src/parse_tree.cpp @@ -335,7 +335,10 @@ static inline parse_token_type_t parse_token_type_from_tokenizer_token( return result; } -/// Helper function for dump_tree. +#if 0 +// Disabled for the 2.2.0 release: https://github.com/fish-shell/fish-shell/issues/1809. + +/// Helper function for parse_dump_tree(). static void dump_tree_recursive(const parse_node_tree_t &nodes, const wcstring &src, node_offset_t node_idx, size_t indent, wcstring *result, size_t *line, node_offset_t *inout_first_node_not_dumped) { @@ -411,6 +414,7 @@ wcstring parse_dump_tree(const parse_node_tree_t &nodes, const wcstring &src) { } return result; } +#endif /// Struct representing elements of the symbol stack, used in the internal state of the LL parser. struct parse_stack_element_t { @@ -467,7 +471,7 @@ class parse_ll_t { // failure (e.g. it is not an unclosed block). bool report_error_for_unclosed_block(); - void dump_stack(void) const; + // void dump_stack(void) const; /// Get the node corresponding to the top element of the stack. parse_node_t &node_for_top_symbol() { @@ -586,6 +590,7 @@ class parse_ll_t { void acquire_output(parse_node_tree_t *output, parse_error_list_t *errors); }; +#if 0 void parse_ll_t::dump_stack(void) const { // Walk backwards from the top, looking for parents. wcstring_list_t lines; @@ -609,6 +614,7 @@ void parse_ll_t::dump_stack(void) const { fprintf(stderr, " %ls\n", lines.at(idx).c_str()); } } +#endif // Give each node a source range equal to the union of the ranges of its children. Terminal nodes // already have source ranges (and no children). Since children always appear after their parents, diff --git a/src/parser.cpp b/src/parser.cpp index 90211df1e..86026f80b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -75,7 +75,7 @@ class io_chain_t; /// Unknown block description. #define UNKNOWN_BLOCK N_(L"unknown/invalid block") -/// Datastructure to describe a block type, like while blocks, command substitution blocks, etc. +/// Data structure to describe a block type, like while blocks, command substitution blocks, etc. struct block_lookup_entry { // The block type id. The legal values are defined in parser.h. block_type_t type; @@ -225,6 +225,8 @@ const wchar_t *parser_t::get_block_desc(int block) const { return _(UNKNOWN_BLOCK); } +#if 0 +// TODO: Lint says this isn't used (which is true). Should this be removed? wcstring parser_t::block_stack_description() const { wcstring result; size_t idx = this->block_count(); @@ -241,6 +243,7 @@ wcstring parser_t::block_stack_description() const { } return result; } +#endif const block_t *parser_t::block_at_index(size_t idx) const { // Zero corresponds to the last element in our vector. @@ -253,11 +256,7 @@ block_t *parser_t::block_at_index(size_t idx) { return idx < count ? block_stack.at(count - idx - 1) : NULL; } -const block_t *parser_t::current_block() const { - return block_stack.empty() ? NULL : block_stack.back(); -} - -block_t *parser_t::current_block() { return block_stack.empty() ? NULL : block_stack.back(); } +block_t *const parser_t::current_block() { return block_stack.empty() ? NULL : block_stack.back(); } void parser_t::forbid_function(const wcstring &function) { forbidden_function.push_back(function); } diff --git a/src/parser.h b/src/parser.h index 4a6823061..d3fd88037 100644 --- a/src/parser.h +++ b/src/parser.h @@ -193,8 +193,11 @@ class parser_t { /// The list of blocks, allocated with new. It's our responsibility to delete these. std::vector block_stack; +#if 0 +// TODO: Lint says this isn't used (which is true). Should this be removed? /// Gets a description of the block stack, for debugging. wcstring block_stack_description() const; +#endif /// List of profile items, allocated with new. std::vector profile_items; @@ -269,8 +272,7 @@ class parser_t { block_t *block_at_index(size_t idx); /// Returns the current (innermost) block. - const block_t *current_block() const; - block_t *current_block(); + block_t *const current_block(); /// Count of blocks. size_t block_count() const { return block_stack.size(); } diff --git a/src/proc.cpp b/src/proc.cpp index 1a298b210..ec1d86d50 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -167,7 +167,7 @@ static pthread_mutex_t job_id_lock = PTHREAD_MUTEX_INITIALIZER; static std::vector consumed_job_ids; job_id_t acquire_job_id(void) { - scoped_lock lock(job_id_lock); + scoped_lock lock(job_id_lock); //!OCLINT(has side effects) // Find the index of the first 0 slot. std::vector::iterator slot = @@ -186,7 +186,7 @@ job_id_t acquire_job_id(void) { void release_job_id(job_id_t jid) { assert(jid > 0); - scoped_lock lock(job_id_lock); + scoped_lock lock(job_id_lock); //!OCLINT(has side effects) 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. @@ -649,61 +649,35 @@ int job_reap(bool interactive) { /// Get the CPU time for the specified process. unsigned long proc_get_jiffies(process_t *p) { - wchar_t fn[FN_SIZE]; + if (p->pid <= 0) return 0; + 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 " - - "%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); - - // Don't need to check exit status of fclose on read-only streams. + // TODO: replace the use of fscanf() as it is brittle and should never be used. + int count = fscanf(f, + "%9d %1023s %c %9d %9d %9d %9d %9d %9lu " + "%9lu %9lu %9lu %9lu %9lu %9lu %9ld %9ld %9ld " + "%9ld %9ld %9ld %9lu %9lu %9ld %9lu %9lu %9lu " + "%9lu %9lu %9lu %9lu %9lu %9lu %9lu %9lu %9lu " + "%9lu %9d %9d ", + &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); fclose(f); - - if (count < 17) { - return 0; - } - + if (count < 17) return 0; return utime + stime + cutime + cstime; } diff --git a/src/signal.cpp b/src/signal.cpp index 8aa558288..60066d3d2 100644 --- a/src/signal.cpp +++ b/src/signal.cpp @@ -198,11 +198,13 @@ static void default_handler(int signal, siginfo_t *info, void *context) { } } +#ifdef SIGWINCH /// Respond to a winch signal by checking the terminal size. static void handle_winch(int sig, siginfo_t *info, void *context) { common_handle_winch(sig); default_handler(sig, 0, 0); } +#endif /// Respond to a hup signal by exiting, unless it is caught by a shellscript function, in which case /// we do nothing. diff --git a/src/util.cpp b/src/util.cpp index bf44feabe..f13334807 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include diff --git a/src/wutil.cpp b/src/wutil.cpp index 3b0bdb2e6..02e975b2c 100644 --- a/src/wutil.cpp +++ b/src/wutil.cpp @@ -415,7 +415,7 @@ const wchar_t *wgettext(const wchar_t *in) { wgettext_init_if_necessary(); wcstring key = in; - scoped_lock lock(wgettext_lock); + scoped_lock lock(wgettext_lock); //!OCLINT(has side effects) wcstring &val = wgettext_map[key]; if (val.empty()) { From bb11999bf7cf56aded4dd55f29bd476b0844e134 Mon Sep 17 00:00:00 2001 From: David Adam Date: Mon, 30 May 2016 16:00:23 +0800 Subject: [PATCH 341/363] license.hdr: remove strlcat license information Function and code removed in 5bf1b0e5f [ci skip] --- doc_src/license.hdr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc_src/license.hdr b/doc_src/license.hdr index 9d3c12481..e372104f1 100644 --- a/doc_src/license.hdr +++ b/doc_src/license.hdr @@ -134,9 +134,9 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ---- -## License for wcslcat and wcslcpy, and code derived from tmux +## License for wcslcpy and code derived from tmux -`fish` also contains small amounts of code under the OpenBSD license, namely versions of the two functions strlcat and strlcpy, modified for use with wide character strings. This code is copyrighted by Todd C. Miller (1998). It also contains code from [tmux](http://tmux.sourceforge.net), copyrighted by Nicholas Marriott (2007), and made available under an identical license. +`fish` also contains small amounts of code under the OpenBSD license, namely a version of the function strlcpy, modified for use with wide character strings. This code is copyrighted by Todd C. Miller (1998). It also contains code from [tmux](http://tmux.sourceforge.net), copyrighted by Nicholas Marriott (2007), and made available under an identical license. The OpenBSD license is included below. From 2768d2ea06e03ae59c99da0fd4d4db582855bc35 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Mon, 30 May 2016 16:36:25 +0200 Subject: [PATCH 342/363] Style fixes for fish_vi_cursor "$fcn" [ci skip] --- share/functions/fish_vi_cursor.fish | 91 +++++++++++++++-------------- 1 file changed, 46 insertions(+), 45 deletions(-) diff --git a/share/functions/fish_vi_cursor.fish b/share/functions/fish_vi_cursor.fish index 5a7b9c8b2..d14ddb1fe 100644 --- a/share/functions/fish_vi_cursor.fish +++ b/share/functions/fish_vi_cursor.fish @@ -1,50 +1,51 @@ function fish_vi_cursor -d 'Set cursor shape for different vi modes' - set -l terminal $argv[1] - set -q terminal[1]; or set terminal auto - set -l uses_echo + set -l terminal $argv[1] + set -q terminal[1] + or set terminal auto + set -l uses_echo - set fcn - switch "$terminal" - case auto - if begin; set -q KONSOLE_PROFILE_NAME - or set -q ITERM_PROFILE; end - set fcn __fish_cursor_konsole - set uses_echo 1 - else if string match -q "xterm*" -- $TERM - set fcn __fish_cursor_xterm - set uses_echo 1 - else - return 1 - end - case konsole - set fcn __fish_cursor_konsole - set uses_echo 1 - case xterm - set fcn __fish_cursor_xterm - set uses_echo 1 - end - - set -l tmux_prefix - set -l tmux_postfix - if begin; set -q TMUX; and set -q uses_echo[1]; end - set tmux_prefix echo -ne "'\ePtmux;\e'" - set tmux_postfix echo -ne "'\e\\\\'" - end - - set -q fish_cursor_unknown - or set -g fish_cursor_unknown block blink - - echo " - function fish_vi_cursor_handle --on-variable fish_bind_mode - set -l varname fish_cursor_\$fish_bind_mode - if not set -q \$varname - set varname fish_cursor_unknown + set -l function + switch "$terminal" + case auto + if set -q KONSOLE_PROFILE_NAME + or set -q ITERM_PROFILE + set function __fish_cursor_konsole + set uses_echo 1 + else if string match -q "xterm*" -- $TERM + set function __fish_cursor_xterm + set uses_echo 1 + else + return 1 + end + case konsole + set function __fish_cursor_konsole + set uses_echo 1 + case xterm + set function __fish_cursor_xterm + set uses_echo 1 end - #echo \$varname \$\$varname - $tmux_prefix - $fcn \$\$varname - $tmux_postfix - end - " | source + + set -l tmux_prefix + set -l tmux_postfix + if set -q TMUX + and set -q uses_echo[1] + set tmux_prefix echo -ne "'\ePtmux;\e'" + set tmux_postfix echo -ne "'\e\\\\'" + end + + set -q fish_cursor_unknown + or set -g fish_cursor_unknown block blink + + echo " + function fish_vi_cursor_handle --on-variable fish_bind_mode + set -l varname fish_cursor_\$fish_bind_mode + if not set -q \$varname + set varname fish_cursor_unknown + end + $tmux_prefix + $function \$\$varname + $tmux_postfix + end + " | source end From 3d74b160b3f1a7c424f38b815320b14bc712eb69 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Mon, 30 May 2016 17:31:41 -0700 Subject: [PATCH 343/363] simplify some fish_tests code This came to my attention because cppcheck was warning about possibly dereferncing a NULL pointer. --- src/fish_tests.cpp | 45 ++++++++++++++------------------------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 29040e227..d753ac78d 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -845,35 +845,25 @@ static void test_utf82wchar(const char *src, size_t slen, const wchar_t *dst, si } } - if (dst != NULL) { + if (!dst) { + size = utf8_to_wchar(src, slen, NULL, flags); + } else { mem = (wchar_t *)malloc(dlen * sizeof(*mem)); if (mem == NULL) { err(L"u2w: %s: MALLOC FAILED\n", descr); return; } + + std::wstring buff; + size = utf8_to_wchar(src, slen, &buff, flags); + std::copy(buff.begin(), buff.begin() + std::min(dlen, buff.size()), mem); } - do { - if (mem == NULL) { - size = utf8_to_wchar(src, slen, NULL, flags); - } else { - std::wstring buff; - size = utf8_to_wchar(src, slen, &buff, flags); - std::copy(buff.begin(), buff.begin() + std::min(dlen, buff.size()), mem); - } - if (res != size) { - err(L"u2w: %s: FAILED (rv: %lu, must be %lu)", descr, size, res); - break; - } - - if (mem == NULL) break; /* OK */ - - if (memcmp(mem, dst, size * sizeof(*mem)) != 0) { - err(L"u2w: %s: BROKEN", descr); - break; - } - - } while (0); + if (res != size) { + err(L"u2w: %s: FAILED (rv: %lu, must be %lu)", descr, size, res); + } else if (mem && memcmp(mem, dst, size * sizeof(*mem)) != 0) { + err(L"u2w: %s: BROKEN", descr); + } free(mem); } @@ -903,7 +893,7 @@ static void test_wchar2utf8(const wchar_t *src, size_t slen, const char *dst, si } } - if (dst != NULL) { + if (dst) { mem = (char *)malloc(dlen); if (mem == NULL) { err(L"w2u: %s: MALLOC FAILED", descr); @@ -914,17 +904,10 @@ static void test_wchar2utf8(const wchar_t *src, size_t slen, const char *dst, si size = wchar_to_utf8(src, slen, mem, dlen, flags); if (res != size) { err(L"w2u: %s: FAILED (rv: %lu, must be %lu)", descr, size, res); - goto finish; - } - - if (mem == NULL) goto finish; /* OK */ - - if (memcmp(mem, dst, size) != 0) { + } else if (dst && memcmp(mem, dst, size) != 0) { err(L"w2u: %s: BROKEN", descr); - goto finish; } -finish: free(mem); } From 8d6735cb4194094b6f3e41573bbfffeb3d49d6ea Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 31 May 2016 23:14:03 +0200 Subject: [PATCH 344/363] Make string match -rnv work Fixes #3098. --- src/builtin_string.cpp | 8 ++++++-- tests/string.in | 2 ++ tests/string.out | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/builtin_string.cpp b/src/builtin_string.cpp index edb6320fe..a2ac02443 100644 --- a/src/builtin_string.cpp +++ b/src/builtin_string.cpp @@ -376,8 +376,12 @@ class pcre2_matcher_t : public string_matcher_t { // Return values: -1 = error, 0 = no match, 1 = match. if (pcre2_rc == PCRE2_ERROR_NOMATCH) { if (opts.invert_match && !opts.quiet) { - streams.out.append(arg); - streams.out.push_back(L'\n'); + if (opts.index) { + streams.out.append_format(L"1 %lu\n", wcslen(arg)); + } else { + streams.out.append(arg); + streams.out.push_back(L'\n'); + } } return opts.invert_match ? 1 : 0; diff --git a/tests/string.in b/tests/string.in index ac858618d..15feb4f55 100644 --- a/tests/string.in +++ b/tests/string.in @@ -88,3 +88,5 @@ string length 2>/dev/null; or echo "missing argument returns 0" string match -r -v "[dcantg].*" dog can cat diz; or echo "no regexp invert match" string match -v "???" dog can cat diz; or echo "no glob invert match" + +string match -rvn a bbb diff --git a/tests/string.out b/tests/string.out index bd3fff457..7afc267ff 100644 --- a/tests/string.out +++ b/tests/string.out @@ -62,3 +62,4 @@ invalid argument error missing argument returns 0 no regexp invert match no glob invert match +1 3 From 8d11bb9f865027ad98c3396e7b3060c76d275c4e Mon Sep 17 00:00:00 2001 From: Fahri Cihan Demirci Date: Wed, 1 Jun 2016 01:56:22 +0300 Subject: [PATCH 345/363] Add Purge Subcommand Completion for Apt (#3097) --- share/completions/apt.fish | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/share/completions/apt.fish b/share/completions/apt.fish index 0db6efe75..c0bd654ec 100644 --- a/share/completions/apt.fish +++ b/share/completions/apt.fish @@ -2,7 +2,7 @@ function __fish_apt_no_subcommand --description 'Test if apt has yet to be given the subcommand' for i in (commandline -opc) - if contains -- $i update upgrade full-upgrade search list install show remove edit-sources + if contains -- $i update upgrade full-upgrade search list install show remove edit-sources purge return 1 end end @@ -11,7 +11,7 @@ end function __fish_apt_use_package --description 'Test if apt command should have packages as potential completion' for i in (commandline -opc) - if contains -- $i contains install remove upgrade full-upgrade show search + if contains -- $i install remove upgrade full-upgrade show search purge return 0 end end @@ -63,3 +63,6 @@ __fish_apt_subcommand upgrade -r --description 'Upgrade packages' # Full Upgrade __fish_apt_subcommand full-upgrade -r --description 'Upgrade packages, removing others when needed' + +# Purge +__fish_apt_subcommand purge -x --description 'Remove packages and delete their config files' From 29c38d73a2945d0d5991083d9a3b542bc61f22ea Mon Sep 17 00:00:00 2001 From: Jens Fredskov Date: Wed, 1 Jun 2016 23:58:38 +0200 Subject: [PATCH 346/363] correct __fish_contains_opts to __fish_contains_opt (#3102) Completion throws and error about the command `__fish_contains_opts` beings unknown. It seems to be a simple typo, as all other completions use `__fish_contains_opt` --- share/completions/aura.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/completions/aura.fish b/share/completions/aura.fish index d5b81dbbf..8ccb43d67 100644 --- a/share/completions/aura.fish +++ b/share/completions/aura.fish @@ -14,7 +14,7 @@ set -l upgrade '__fish_contains_opt -s U upgrade' set -l aur '__fish_contains_opt -s A aursync' set -l abs '__fish_contains_opt -s M abssync' set -l save '__fish_contains_opt -s B save' -set -l downgrade '__fish_contains_opts -s C downgrade' +set -l downgrade '__fish_contains_opt -s C downgrade' set -l orphans '__fish_contains_opt -s O orphans' set -l logfile '__fish_contains_opt -s L viewlog' set -l search '__fish_contains_opt -s s search' From 22e0702e8dcbcba57f499015ede690659a5ffa0b Mon Sep 17 00:00:00 2001 From: David Adam Date: Thu, 2 Jun 2016 08:19:26 +0800 Subject: [PATCH 347/363] travis: disable clang build Clang repositories currently offline, causing build errors. Can be reverted once https://github.com/travis-ci/travis-ci/issues/6120 is fixed. --- .travis.yml | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8307fe85f..e322ce491 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,24 +13,26 @@ matrix: - gettext - libncurses5-dev - - os: linux - compiler: clang - addons: - apt: - sources: - - llvm-toolchain-precise-3.8 - - ubuntu-toolchain-r-test - packages: - - clang-3.8 - - llvm-3.8 # for llvm-symbolizer - - bc - - expect - - gettext - - libncurses5-dev - env: - - CXXFLAGS="-g -fno-omit-frame-pointer -fsanitize=address" - - ASAN_OPTIONS=check_initialization_order=1:detect_stack_use_after_return=1:detect_leaks=1 - before_install: export CXX=clang++-3.8 + # Disable for now until + # https://github.com/travis-ci/travis-ci/issues/6120 fixed + #- os: linux + # compiler: clang + # addons: + # apt: + # sources: + # - llvm-toolchain-precise-3.8 + # - ubuntu-toolchain-r-test + # packages: + # - clang-3.8 + # - llvm-3.8 # for llvm-symbolizer + # - bc + # - expect + # - gettext + # - libncurses5-dev + # env: + # - CXXFLAGS="-g -fno-omit-frame-pointer -fsanitize=address" + # - ASAN_OPTIONS=check_initialization_order=1:detect_stack_use_after_return=1:detect_leaks=1 + # before_install: export CXX=clang++-3.8 - os: osx before_install: From aee9d2c9d7c558268717d887683529399bd8d95f Mon Sep 17 00:00:00 2001 From: Jorge Bucaran Date: Tue, 31 May 2016 01:05:19 +0900 Subject: [PATCH 348/363] Do not hardcode RGB values in color definitions. --- share/functions/__fish_config_interactive.fish | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/share/functions/__fish_config_interactive.fish b/share/functions/__fish_config_interactive.fish index 5d634e778..d9b4dc068 100644 --- a/share/functions/__fish_config_interactive.fish +++ b/share/functions/__fish_config_interactive.fish @@ -37,9 +37,9 @@ function __fish_config_interactive -d "Initializations that should be performed set -q fish_color_normal or set -U fish_color_normal normal set -q fish_color_command - or set -U fish_color_command 005fd7 purple + or set -U fish_color_command brblue set -q fish_color_param - or set -U fish_color_param 00afff cyan + or set -U fish_color_param cyan set -q fish_color_redirection or set -U fish_color_redirection normal set -q fish_color_comment @@ -55,7 +55,7 @@ function __fish_config_interactive -d "Initializations that should be performed set -q fish_color_quote or set -U fish_color_quote brown set -q fish_color_autosuggestion - or set -U fish_color_autosuggestion 555 yellow + or set -U fish_color_autosuggestion brgrey set -q fish_color_user or set -U fish_color_user green @@ -86,8 +86,8 @@ function __fish_config_interactive -d "Initializations that should be performed or set -U fish_pager_color_prefix cyan set -q fish_pager_color_completion or set -U fish_pager_color_completion normal - set -q fish_pager_color_description 555 - or set -U fish_pager_color_description 555 yellow + set -q fish_pager_color_description + or set -U fish_pager_color_description brgrey set -q fish_pager_color_progress or set -U fish_pager_color_progress cyan From db1ec847f989a71b2cb1e0c39194e277613931fd Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Wed, 1 Jun 2016 22:03:27 -0700 Subject: [PATCH 349/363] fix lint error in wgettext() Cppcheck was complaining about the `return val.c_str()` at the end of the `wgettext()` function. That would normally a bug since the lifetime of `val` ends when the function returns. In this particular case that's not true because the string is interned in a cache. Nonetheless, rather than suppress the lint warning I decided to modify the API to be more idiomatic. In the process of fixing the aforementioned lint warning I fixed several other lint errors in that module. This required making our copy of `wgetopt()` compatible with the rest of the fish code. Specifically, by removing its local definitions of the "_" macro so it uses the same macro used everywhere else in the fish code. The sooner we kill the use of wide chars the better. --- configure.ac | 12 ------------ src/common.h | 4 ++-- src/complete.cpp | 11 +++++++---- src/wgetopt.cpp | 22 +++------------------- src/wutil.cpp | 35 ++++++++++++++++++----------------- src/wutil.h | 2 +- 6 files changed, 31 insertions(+), 55 deletions(-) diff --git a/configure.ac b/configure.ac index dae16589a..8f1b95fad 100644 --- a/configure.ac +++ b/configure.ac @@ -237,18 +237,6 @@ LDFLAGS="$prev_LDFLAGS" AC_CHECK_FILES([/proc/self/stat]) - -# -# This is ued to tell the wgetopt library to translate strings. This -# way wgetopt can be dropped into any project without requiring i18n. -# - -AC_DEFINE( - [HAVE_TRANSLATE_H], - [1], - [Define to 1 if the wgettext function should be used for translating strings.] -) - # Disable curses macros that conflict with the STL AC_DEFINE([NCURSES_NOMACROS], [1], [Define to 1 to disable ncurses macros that conflict with the STL]) AC_DEFINE([NOMACROS], [1], [Define to 1 to disable curses macros that conflict with the STL]) diff --git a/src/common.h b/src/common.h index e59dfc0e8..073424362 100644 --- a/src/common.h +++ b/src/common.h @@ -226,8 +226,8 @@ void write_ignore(int fd, const void *buff, size_t count); return retval; \ } -/// Shorthand for wgettext call. -#define _(wstr) wgettext(wstr) +/// Shorthand for wgettext call in situations where a C-style string is needed (e.g., fwprintf()). +#define _(wstr) wgettext(wstr).c_str() /// Noop, used to tell xgettext that a string should be translated, even though it is not directly /// sent to wgettext. diff --git a/src/complete.cpp b/src/complete.cpp index 6062a055b..f686ab313 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -56,7 +56,9 @@ /// since it can occur, and should not be translated. (Gettext returns the version information as /// the response). #ifdef USE_GETTEXT -static const wchar_t *C_(const wcstring &s) { return s.empty() ? L"" : wgettext(s.c_str()); } +static const wchar_t *C_(const wcstring &s) { + return s.empty() ? L"" : wgettext(s.c_str()).c_str(); +} #else static const wcstring &C_(const wcstring &s) { return s; } #endif @@ -193,12 +195,13 @@ completion_t::~completion_t() {} // Clear the COMPLETE_AUTO_SPACE flag, and set COMPLETE_NO_SPACE appropriately depending on the // suffix of the string. static complete_flags_t resolve_auto_space(const wcstring &comp, complete_flags_t flags) { + complete_flags_t new_flags = flags; if (flags & COMPLETE_AUTO_SPACE) { - flags = flags & ~COMPLETE_AUTO_SPACE; + new_flags &= ~COMPLETE_AUTO_SPACE; size_t len = comp.size(); - if (len > 0 && (wcschr(L"/=@:", comp.at(len - 1)) != 0)) flags |= COMPLETE_NO_SPACE; + if (len > 0 && (wcschr(L"/=@:", comp.at(len - 1)) != 0)) new_flags |= COMPLETE_NO_SPACE; } - return flags; + return new_flags; } // completion_t functions. Note that the constructor resolves flags! diff --git a/src/wgetopt.cpp b/src/wgetopt.cpp index f7ed54274..f31b623b0 100644 --- a/src/wgetopt.cpp +++ b/src/wgetopt.cpp @@ -36,7 +36,7 @@ // You should have received a copy of the GNU Library General Public License along with the GNU C // Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 675 Mass // Ave, Cambridge, MA 02139, USA. -#include "config.h" +#include "config.h" // IWYU pragma: keep #include #include @@ -62,21 +62,6 @@ #include "wgetopt.h" #include "wutil.h" // IWYU pragma: keep -// Use translation functions if available. -#ifdef _ -#undef _ -#endif - -#ifdef HAVE_TRANSLATE_H -#ifdef USE_GETTEXT -#define _(string) wgettext(string) -#else -#define _(string) (string) -#endif -#else -#define _(wstr) wstr -#endif - #ifdef __GNU_LIBRARY__ // We want to avoid inclusion of string.h with non-GNU libraries because there are many ways it can // cause trouble. On some systems, it contains special magic macros that don't work in GCC. @@ -306,9 +291,8 @@ int wgetopter_t::_wgetopt_internal(int argc, wchar_t **argv, const wchar_t *opts int indfound = 0; // set to zero by Anton int option_index; - for (nameend = nextchar; *nameend && *nameend != '='; nameend++) { - // Do nothing. - } + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + ; //!OCLINT(empty body) // Test all long options for either exact match or abbreviated matches. for (p = longopts, option_index = 0; p->name; p++, option_index++) diff --git a/src/wutil.cpp b/src/wutil.cpp index 02e975b2c..5e1ecea80 100644 --- a/src/wutil.cpp +++ b/src/wutil.cpp @@ -166,7 +166,6 @@ FILE *wfopen(const wcstring &path, const char *mode) { default: { errno = EINVAL; return NULL; - break; } } // Skip binary. @@ -196,18 +195,22 @@ bool set_cloexec(int fd) { static int wopen_internal(const wcstring &pathname, int flags, mode_t mode, bool cloexec) { ASSERT_IS_NOT_FORKED_CHILD(); cstring tmp = wcs2string(pathname); -// Prefer to use O_CLOEXEC. It has to both be defined and nonzero. + int fd; + #ifdef O_CLOEXEC - if (cloexec && (O_CLOEXEC != 0)) { - flags |= O_CLOEXEC; - cloexec = false; + // Prefer to use O_CLOEXEC. It has to both be defined and nonzero. + if (cloexec) { + fd = open(tmp.c_str(), flags | O_CLOEXEC, mode); + } else { + fd = open(tmp.c_str(), flags, mode); } -#endif - int fd = ::open(tmp.c_str(), flags, mode); - if (cloexec && fd >= 0 && !set_cloexec(fd)) { +#else + fd = open(tmp.c_str(), flags, mode); + if (fd >= 0 && !set_cloexec(fd)) { close(fd); fd = -1; } +#endif return fd; } @@ -251,7 +254,8 @@ void wperror(const wchar_t *s) { int make_fd_nonblocking(int fd) { int flags = fcntl(fd, F_GETFL, 0); int err = 0; - if (!(flags & O_NONBLOCK)) { + bool nonblocking = flags & O_NONBLOCK; + if (!nonblocking) { err = fcntl(fd, F_SETFL, flags | O_NONBLOCK); } return err == -1 ? errno : 0; @@ -260,7 +264,8 @@ int make_fd_nonblocking(int fd) { int make_fd_blocking(int fd) { int flags = fcntl(fd, F_GETFL, 0); int err = 0; - if (flags & O_NONBLOCK) { + bool nonblocking = flags & O_NONBLOCK; + if (nonblocking) { err = fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); } return err == -1 ? errno : 0; @@ -406,17 +411,13 @@ static void wgettext_init_if_necessary() { pthread_once(&once, wgettext_really_init); } -const wchar_t *wgettext(const wchar_t *in) { - if (!in) return in; - +const wcstring &wgettext(const wchar_t *in) { // Preserve errno across this since this is often used in printing error messages. int err = errno; + wcstring key = in; wgettext_init_if_necessary(); - - wcstring key = in; scoped_lock lock(wgettext_lock); //!OCLINT(has side effects) - wcstring &val = wgettext_map[key]; if (val.empty()) { cstring mbs_in = wcs2string(key); @@ -427,7 +428,7 @@ const wchar_t *wgettext(const wchar_t *in) { // The returned string is stored in the map. // TODO: If we want to shrink the map, this would be a problem. - return val.c_str(); + return val; } int wmkdir(const wcstring &name, int mode) { diff --git a/src/wutil.h b/src/wutil.h index 9fc610964..b5c77870d 100644 --- a/src/wutil.h +++ b/src/wutil.h @@ -84,7 +84,7 @@ std::wstring wbasename(const std::wstring &path); /// gettext function, wgettext takes care of setting the correct domain, etc. using the textdomain /// and bindtextdomain functions. This should probably be moved out of 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 wcstring &wgettext(const wchar_t *in); /// Wide character version of mkdir. int wmkdir(const wcstring &dir, int mode); From 24d6f6d06637e1045a819329e5505eb2e43fb1bc Mon Sep 17 00:00:00 2001 From: Hunsu Date: Thu, 2 Jun 2016 12:49:01 +0200 Subject: [PATCH 350/363] Add completions for git blame command (#3094) --- share/completions/git.fish | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/share/completions/git.fish b/share/completions/git.fish index dd3cc46ba..8c5216638 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -645,5 +645,31 @@ complete -f -c git -n '__fish_git_using_command clean' -s x -d 'Remove ignored f complete -f -c git -n '__fish_git_using_command clean' -s X -d 'Remove only ignored files' # TODO -e option +### git blame +complete -f -c git -n '__fish_git_needs_command' -a blame -d 'Show what revision and author last modified each line of a file' +complete -f -c git -n '__fish_git_using_command blame' -s b -d 'Show blank SHA-1 for boundary commits' +complete -f -c git -n '__fish_git_using_command blame' -l root -d 'Do not treat root commits as boundaries' +complete -f -c git -n '__fish_git_using_command blame' -l show-stats -d 'Include additional statistics' +complete -f -c git -n '__fish_git_using_command blame' -s L -d 'Annotate only the given line range' +complete -f -c git -n '__fish_git_using_command blame' -s l -d 'Show long rev' +complete -f -c git -n '__fish_git_using_command blame' -s t -d 'Show raw timestamp' +complete -r -c git -n '__fish_git_using_command blame' -s S -d 'Use revisions from named file instead of calling rev-list' +complete -f -c git -n '__fish_git_using_command blame' -l reverse -d 'Walk history forward instead of backward' +complete -f -c git -n '__fish_git_using_command blame' -s p -l porcelain -d 'Show in a format designed for machine consumption' +complete -f -c git -n '__fish_git_using_command blame' -l line-porcelain -d 'Show the porcelain format' +complete -f -c git -n '__fish_git_using_command blame' -l incremental -d 'Show the result incrementally' +complete -r -c git -n '__fish_git_using_command blame' -l contents -d 'Instead of working tree, use the contents of the named file' +complete -x -c git -n '__fish_git_using_command blame' -l date -d 'Specifies the format used to output dates' +complete -f -c git -n '__fish_git_using_command blame' -s M -d 'Detect moved or copied lines within a file' +complete -f -c git -n '__fish_git_using_command blame' -s C -d 'Detect lines moved or copied from other files that were modified in the same commit' +complete -f -c git -n '__fish_git_using_command blame' -s h -d 'Show help message' +complete -f -c git -n '__fish_git_using_command blame' -s c -d 'Use the same output mode as git-annotate' +complete -f -c git -n '__fish_git_using_command blame' -s f -l show-name -d 'Show the filename in the original commit' +complete -f -c git -n '__fish_git_using_command blame' -s n -l show-number -d 'Show the line number in the original commit' +complete -f -c git -n '__fish_git_using_command blame' -s s -d 'Suppress the author name and timestamp from the output' +complete -f -c git -n '__fish_git_using_command blame' -s e -l show-email -d 'Show the author email instead of author name' +complete -f -c git -n '__fish_git_using_command blame' -s w -d 'Ignore whitespace changes' + + ## Custom commands (git-* commands installed in the PATH) complete -c git -n '__fish_git_needs_command' -a '(__fish_git_custom_commands)' -d 'Custom command' From 63a851cfd6f6be2ecfaf695495ed788b8115fb9d Mon Sep 17 00:00:00 2001 From: Beni Cherniavsky-Paskin Date: Thu, 2 Jun 2016 00:00:19 +0300 Subject: [PATCH 351/363] mention nullglob exceptions in failglob error msg --- src/parse_constants.h | 2 +- tests/test5.err | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/parse_constants.h b/src/parse_constants.h index 01580d151..8381f2ae0 100644 --- a/src/parse_constants.h +++ b/src/parse_constants.h @@ -209,7 +209,7 @@ void parse_error_offset_source_start(parse_error_list_t *errors, size_t amt); #define ILLEGAL_FD_ERR_MSG _(L"Illegal file descriptor in redirection '%ls'") /// Error message for wildcards with no matches. -#define WILDCARD_ERR_MSG _(L"No matches for wildcard '%ls'.") +#define WILDCARD_ERR_MSG _(L"No matches for wildcard '%ls'. (Tip: empty matches are allowed in 'set', 'count', 'for'.)") /// Error when using break outside of loop. #define INVALID_BREAK_ERR_MSG _(L"'break' while not inside of loop") diff --git a/tests/test5.err b/tests/test5.err index 7dba999cf..58849bd19 100644 --- a/tests/test5.err +++ b/tests/test5.err @@ -1,3 +1,3 @@ -No matches for wildcard '*ee*'. +No matches for wildcard '*ee*'. (Tip: empty matches are allowed in 'set', 'count', 'for'.) fish: case *ee* ^ From 57f289850c24a44a2590cb30762853f2a61f4efd Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Mon, 30 May 2016 12:56:04 +0200 Subject: [PATCH 352/363] Don't insert prefix for non-prefix matches The issue here is that when inserting a common prefix for e.g. a substring match, we increase the amount of available candidates again to things the user didn't want. An example is in share/functions - a completion for "inter" would previously expand to "__fish_" because it matched: - __fish_config_interactive.fish - __fish_print_interfaces.fish - __fish_print_lpr_printers.fish The completion afterwards would then show 189 possible matches, only three of which (the above) actually matched the original "inter". Fixes #3089. --- src/reader.cpp | 87 ++++++++++++++++++++++++++------------------------ 1 file changed, 45 insertions(+), 42 deletions(-) diff --git a/src/reader.cpp b/src/reader.cpp index b67f290bc..968a508a8 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -1433,53 +1433,56 @@ static bool handle_completions(const std::vector &comp, surviving_completions.push_back(el); } - // Try to find a common prefix to insert among the surviving completions. - wcstring common_prefix; - complete_flags_t flags = 0; - bool prefix_is_partial_completion = false; - for (size_t i = 0; i < surviving_completions.size(); i++) { - const completion_t &el = surviving_completions.at(i); - if (i == 0) { - // First entry, use the whole string. - common_prefix = el.completion; - flags = el.flags; - } else { - // Determine the shared prefix length. - size_t idx, max = mini(common_prefix.size(), el.completion.size()); - for (idx = 0; idx < max; idx++) { - wchar_t ac = common_prefix.at(idx), bc = el.completion.at(idx); - bool matches = (ac == bc); - // If we are replacing the token, allow case to vary. - if (will_replace_token && !matches) { - // Hackish way to compare two strings in a case insensitive way, hopefully - // better than towlower(). - matches = (wcsncasecmp(&ac, &bc, 1) == 0); + bool use_prefix = false; + if (match_type_shares_prefix(best_match_type)) { + // Try to find a common prefix to insert among the surviving completions. + wcstring common_prefix; + complete_flags_t flags = 0; + bool prefix_is_partial_completion = false; + for (size_t i = 0; i < surviving_completions.size(); i++) { + const completion_t &el = surviving_completions.at(i); + if (i == 0) { + // First entry, use the whole string. + common_prefix = el.completion; + flags = el.flags; + } else { + // Determine the shared prefix length. + size_t idx, max = mini(common_prefix.size(), el.completion.size()); + for (idx = 0; idx < max; idx++) { + wchar_t ac = common_prefix.at(idx), bc = el.completion.at(idx); + bool matches = (ac == bc); + // If we are replacing the token, allow case to vary. + if (will_replace_token && !matches) { + // Hackish way to compare two strings in a case insensitive way, + // hopefully better than towlower(). + matches = (wcsncasecmp(&ac, &bc, 1) == 0); + } + if (!matches) break; } - if (!matches) break; + + // idx is now the length of the new common prefix. + common_prefix.resize(idx); + prefix_is_partial_completion = true; + + // Early out if we decide there's no common prefix. + if (idx == 0) break; } - - // idx is now the length of the new common prefix. - common_prefix.resize(idx); - prefix_is_partial_completion = true; - - // Early out if we decide there's no common prefix. - if (idx == 0) break; } - } - // Determine if we use the prefix. We use it if it's non-empty and it will actually make the - // command line longer. It may make the command line longer by virtue of not using - // REPLACE_TOKEN (so it always appends to the command line), or by virtue of replacing the - // token but being longer than it. - bool use_prefix = common_prefix.size() > (will_replace_token ? tok.size() : 0); - assert(!use_prefix || !common_prefix.empty()); + // Determine if we use the prefix. We use it if it's non-empty and it will actually make + // the command line longer. It may make the command line longer by virtue of not using + // REPLACE_TOKEN (so it always appends to the command line), or by virtue of replacing + // the token but being longer than it. + use_prefix = common_prefix.size() > (will_replace_token ? tok.size() : 0); + assert(!use_prefix || !common_prefix.empty()); - if (use_prefix) { - // We got something. If more than one completion contributed, then it means we have a - // prefix; don't insert a space after it. - if (prefix_is_partial_completion) flags |= COMPLETE_NO_SPACE; - completion_insert(common_prefix.c_str(), flags); - success = true; + if (use_prefix) { + // We got something. If more than one completion contributed, then it means we have + // a prefix; don't insert a space after it. + if (prefix_is_partial_completion) flags |= COMPLETE_NO_SPACE; + completion_insert(common_prefix.c_str(), flags); + success = true; + } } if (continue_after_prefix_insertion || !use_prefix) { From 53e865b65434fa9ec12afc11a076aec244cd3c43 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Wed, 1 Jun 2016 20:03:50 -0700 Subject: [PATCH 353/363] put curses/terminfo vars into the environment We need to actually export the curses/terminfo env vars in order for `setupterm()` to be able to use them. While fixing this I reworked the fallback logic implemented by @zanchey in response to issue #1060 in order to simplify the logic and clarify the error messages. This does not allow someone to change the curses/terminfo env vars after the first prompt is displayed (you can but it won't affect the current fish process). It only makes it possible to set `TERM`, `TERMINFO`, and `TERMINFO_DIRS` in *config.fish* or similar config file and have them be honored by fish. --- src/env.cpp | 43 +++++++++++++++++++++++++++++++++++++++---- src/input.cpp | 33 +++++++++++---------------------- src/output.cpp | 13 +++---------- src/output.h | 6 ------ 4 files changed, 53 insertions(+), 42 deletions(-) diff --git a/src/env.cpp b/src/env.cpp index e09064ad7..71d858005 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -124,10 +124,15 @@ static null_terminated_array_t export_array; static bool has_changed_exported = true; static void mark_changed_exported() { has_changed_exported = true; } -/// List of all locale variable names. -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}; +/// List of all locale environment variable names. +static const wchar_t *const locale_variable[] = { + L"LANG", L"LANGUAGE", L"LC_ALL", L"LC_ADDRESS", L"LC_COLLATE", + L"LC_CTYPE", L"LC_IDENTIFICATION", L"LC_MEASUREMENT", L"LC_MESSAGES", L"LC_MONETARY", + L"LC_NAME", L"LC_NUMERIC", L"LC_PAPER", L"LC_TELEPHONE", L"LC_TIME", + NULL}; + +/// List of all curses environment variable names. +static const wchar_t *const curses_variable[] = {L"TERM", L"TERMINFO", L"TERMINFO_DIRS", NULL}; const var_entry_t *env_node_t::find_entry(const wcstring &key) { const var_entry_t *result = NULL; @@ -200,10 +205,40 @@ static void handle_locale() { } } +/// Check if the specified variable is a locale variable. +static bool var_is_curses(const wcstring &key) { + for (size_t i = 0; curses_variable[i]; i++) { + if (key == curses_variable[i]) { + return true; + } + } + return false; +} + +/// Push all curses/terminfo env vars into the global environment where they can be found by those +/// libraries. +static void handle_curses() { + for (size_t i = 0; curses_variable[i]; i++) { + const wchar_t *key = curses_variable[i]; + const env_var_t var = env_get_string(key); + if (!var.empty()) { + const std::string &name = wcs2string(key); + const std::string &value = wcs2string(var); + setenv(name.c_str(), value.c_str(), 1); + } + } + // TODO: Modify input_init() to allow calling it when the terminfo env vars are dynamically + // changed. At the present time it can be called just once. Also, we should really only do this + // if the TERM var is set. + // input_init(); +} + /// React to modifying the given variable. static void react_to_variable_change(const wcstring &key) { if (var_is_locale(key)) { handle_locale(); + } else if (var_is_curses(key)) { + handle_curses(); } else if (key == L"fish_term256" || key == L"fish_term24bit") { update_fish_color_support(); reader_react_to_color_change(); diff --git a/src/input.cpp b/src/input.cpp index 5b599e85d..b9132b2c6 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -1,7 +1,6 @@ // Functions for reading a character of input from stdin. #include "config.h" -#include #include #include #include @@ -339,35 +338,25 @@ void update_fish_color_support(void) { int input_init() { if (is_init) return 1; - is_init = true; - input_common_init(&interrupt_handler); - const env_var_t term = env_get_string(L"TERM"); - int errret; - if (setupterm(const_cast(wcs2string(term).c_str()), STDOUT_FILENO, &errret) == ERR) { - debug(0, _(L"Could not set up terminal")); - if (errret == 0) { - debug(0, _(L"Check that your terminal type, '%ls', is supported on this system"), - term.c_str()); - debug(0, _(L"Attempting to use '%ls' instead"), DEFAULT_TERM); - env_set(L"TERM", DEFAULT_TERM, ENV_GLOBAL | ENV_EXPORT); - const std::string default_term = wcs2string(DEFAULT_TERM); - if (setupterm(const_cast(default_term.c_str()), STDOUT_FILENO, &errret) == - ERR) { - debug(0, _(L"Could not set up terminal")); - exit_without_destructors(1); - } - } else { + int err_ret; + if (setupterm(NULL, STDOUT_FILENO, &err_ret) == ERR) { + env_var_t term = env_get_string(L"TERM"); + debug(0, _(L"Your TERM value of '%ls' is not valid"), term.c_str()); + debug(0, _(L"Check that your terminal type is supported on this system")); + env_set(L"TERM", DEFAULT_TERM, ENV_GLOBAL | ENV_EXPORT); + if (setupterm(NULL, STDOUT_FILENO, &err_ret) == ERR) { + debug(0, _(L"Unable to setup terminal using your TERM or the '%ls' fallback"), + DEFAULT_TERM); exit_without_destructors(1); + } else { + debug(0, _(L"Using fallback terminal type '%ls' instead"), DEFAULT_TERM); } } - assert(!term.missing()); - output_set_term(term); input_terminfo_init(); - update_fish_color_support(); // If we have no keybindings, add a few simple defaults. diff --git a/src/output.cpp b/src/output.cpp index 0d7c74f0b..05238e1ed 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -24,6 +24,7 @@ #include "color.h" #include "common.h" +#include "env.h" #include "fallback.h" // IWYU pragma: keep #include "output.h" #include "wutil.h" // IWYU pragma: keep @@ -33,9 +34,6 @@ static int writeb_internal(char c); /// The function used for output. static int (*out)(char c) = writeb_internal; -/// Name of terminal. -static wcstring current_term; - /// Whether term256 and term24bit are supported. static color_support_t color_support = 0; @@ -416,18 +414,13 @@ rgb_color_t parse_color(const wcstring &val, bool is_background) { return result; } -void output_set_term(const wcstring &term) { current_term.assign(term); } - -const wchar_t *output_get_term() { - return current_term.empty() ? L"" : current_term.c_str(); -} - void writembs_check(char *mbs, const char *mbs_name, const char *file, long line) { if (mbs != NULL) { tputs(mbs, 1, &writeb); } else { + env_var_t term = env_get_string(L"TERM"); debug(0, _(L"Tried to use terminfo string %s on line %ld of %s, which is undefined in " L"terminal of type \"%ls\". Please report this error to %s"), - mbs_name, line, file, output_get_term(), PACKAGE_BUGREPORT); + mbs_name, line, file, term.c_str(), PACKAGE_BUGREPORT); } } diff --git a/src/output.h b/src/output.h index 5882676df..2ecebfbf9 100644 --- a/src/output.h +++ b/src/output.h @@ -76,12 +76,6 @@ void output_set_writer(int (*writer)(char)); /// Return the current output writer. int (*output_get_writer())(char); -/// Set the terminal name. -void output_set_term(const wcstring &term); - -/// Return the terminal name. -const wchar_t *output_get_term(); - /// Sets what colors are supported. enum { color_support_term256 = 1 << 0, color_support_term24bit = 1 << 1 }; typedef unsigned int color_support_t; From 410d92ed6108e881ef40c88dbbf68c274f833d32 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 4 Jun 2016 12:26:06 +0200 Subject: [PATCH 354/363] git completions: Ignore stderr everywhere This allows us to run git commands outside of a git repo. Fixes #3114. --- share/completions/git.fish | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index 8c5216638..093592708 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -6,7 +6,8 @@ function __fish_git_commits # This allows filtering by subject with the new pager! # Because even subject lines can be quite long, # trim them (abbrev'd hash+tab+subject) to 70 characters - command git log --pretty=tformat:"%h"\t"%s" --all | string replace -r '(.{70}).+' '$1...' + command git log --pretty=tformat:"%h"\t"%s" --all ^/dev/null \ + | string replace -r '(.{70}).+' '$1...' end function __fish_git_branches @@ -34,19 +35,19 @@ end function __fish_git_modified_files # git diff --name-only hands us filenames relative to the git toplevel - set -l root (command git rev-parse --show-toplevel) + set -l root (command git rev-parse --show-toplevel ^/dev/null) # Print files from the current $PWD as-is, prepend all others with ":/" (relative to toplevel in git-speak) # This is a bit simplistic but finding the lowest common directory and then replacing everything else in $PWD with ".." is a bit annoying string replace -- "$PWD/" "" "$root/"(command git diff --name-only ^/dev/null) | string replace "$root/" ":/" end function __fish_git_staged_files - set -l root (command git rev-parse --show-toplevel) + set -l root (command git rev-parse --show-toplevel ^/dev/null) string replace -- "$PWD/" "" "$root/"(command git diff --staged --name-only ^/dev/null) | string replace "$root/" ":/" end function __fish_git_add_files - set -l root (command git rev-parse --show-toplevel) + set -l root (command git rev-parse --show-toplevel ^/dev/null) string replace -- "$PWD/" "" "$root/"(command git -C $root ls-files -mo --exclude-standard ^/dev/null) | string replace "$root/" ":/" end @@ -200,7 +201,7 @@ function __fish_git_possible_commithash end function __fish_git_reflog - command git reflog | string replace -r '[0-9a-f]* (.+@\{[0-9]+\}): (.*)$' '$1\t$2' + command git reflog ^/dev/null | string replace -r '[0-9a-f]* (.+@\{[0-9]+\}): (.*)$' '$1\t$2' end # general options From 32a585a52b20687a1a16c45142da2261d28b7a27 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 4 Jun 2016 12:27:06 +0200 Subject: [PATCH 355/363] git completions: Only take general options before command --- share/completions/git.fish | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index 093592708..4e9c9e2df 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -206,24 +206,24 @@ end # general options complete -f -c git -l help -d 'Display the manual of a git command' -complete -f -c git -l version -d 'Display version' -complete -x -c git -s C -a '(__fish_complete_directories)' -d 'Run as if git was started in this directory' -complete -x -c git -s c -a '(command git config -l | string replace = \t)' -d 'Set a configuration option' -complete -x -c git -l exec-path -a '(__fish_git_complete_directories)' -d 'Get or set the path to the git programs' -complete -f -c git -l html-path -d 'Print the path to the html documentation' -complete -f -c git -l man-path -d 'Print the path to the man documentation' -complete -f -c git -l info-path -d 'Print the path to the info documentation' -complete -f -c git -s p -l paginate -d 'Pipe output into a pager' -complete -f -c git -l no-pager -d 'Do not pipe output into a pager' -complete -f -c git -l git-dir -d 'Set the path to the repository' -complete -f -c git -l work-tree -d 'Set the path to the working tree' -complete -f -c git -l namespace -d 'Set the namespace' -complete -f -c git -l bare -d 'Treat the repository as bare' -complete -f -c git -l no-replace-objects -d 'Do not use replacement refs to replace git objects' -complete -f -c git -l literal-pathspecs -d 'Treat pathspecs literally' -complete -f -c git -l glob-pathspecs -d 'Treat pathspecs as globs' -complete -f -c git -l noglob-pathspecs -d "Don't treat pathspecs as globs" -complete -f -c git -l icase-pathspecs -d 'Match pathspecs case-insensitively' +complete -f -c git -n '__fish_git_needs_command' -l version -d 'Display version' +complete -x -c git -n '__fish_git_needs_command' -s C -a '(__fish_complete_directories)' -d 'Run as if git was started in this directory' +complete -x -c git -n '__fish_git_needs_command' -s c -a '(command git config -l ^/dev/null | string replace = \t)' -d 'Set a configuration option' +complete -x -c git -n '__fish_git_needs_command' -l exec-path -a '(__fish_git_complete_directories)' -d 'Get or set the path to the git programs' +complete -f -c git -n '__fish_git_needs_command' -l html-path -d 'Print the path to the html documentation' +complete -f -c git -n '__fish_git_needs_command' -l man-path -d 'Print the path to the man documentation' +complete -f -c git -n '__fish_git_needs_command' -l info-path -d 'Print the path to the info documentation' +complete -f -c git -n '__fish_git_needs_command' -s p -l paginate -d 'Pipe output into a pager' +complete -f -c git -n '__fish_git_needs_command' -l no-pager -d 'Do not pipe output into a pager' +complete -f -c git -n '__fish_git_needs_command' -l git-dir -d 'Set the path to the repository' +complete -f -c git -n '__fish_git_needs_command' -l work-tree -d 'Set the path to the working tree' +complete -f -c git -n '__fish_git_needs_command' -l namespace -d 'Set the namespace' +complete -f -c git -n '__fish_git_needs_command' -l bare -d 'Treat the repository as bare' +complete -f -c git -n '__fish_git_needs_command' -l no-replace-objects -d 'Do not use replacement refs to replace git objects' +complete -f -c git -n '__fish_git_needs_command' -l literal-pathspecs -d 'Treat pathspecs literally' +complete -f -c git -n '__fish_git_needs_command' -l glob-pathspecs -d 'Treat pathspecs as globs' +complete -f -c git -n '__fish_git_needs_command' -l noglob-pathspecs -d "Don't treat pathspecs as globs" +complete -f -c git -n '__fish_git_needs_command' -l icase-pathspecs -d 'Match pathspecs case-insensitively' # Options shared between multiple commands complete -f -c git -n '__fish_git_using_command log show diff-tree rev-list' -l pretty -a 'oneline short medium full fuller email raw format:' From 0b385f145ce6144b5812bd89fa8f73369bcbe57f Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Fri, 3 Jun 2016 19:05:13 -0700 Subject: [PATCH 356/363] simplify, and fix, setting the current locale Fix test setup bogosities. Specifically, they weren't hermetic with respect to locale env vars. Rewrite the handling of locale vars to simplify the code and make it more like the pattern most programs employ. Fixes #3110 --- src/builtin_printf.cpp | 25 +++++------------ src/common.cpp | 10 +------ src/common.h | 7 +++-- src/env.cpp | 60 ++++++++++++++++++++--------------------- src/fish.cpp | 5 ++-- src/fish_indent.cpp | 12 ++++++--- src/fish_key_reader.cpp | 4 +-- tests/test_util.fish | 41 +++++++++++++++++----------- 8 files changed, 77 insertions(+), 87 deletions(-) diff --git a/src/builtin_printf.cpp b/src/builtin_printf.cpp index 36b8e6382..d81ab4534 100644 --- a/src/builtin_printf.cpp +++ b/src/builtin_printf.cpp @@ -197,24 +197,6 @@ static int octal_to_bin(wchar_t c) { } } -double C_STRTOD(wchar_t const *nptr, wchar_t **endptr) { - double r; - - const wcstring saved_locale = wsetlocale(LC_NUMERIC, NULL); - - if (!saved_locale.empty()) { - wsetlocale(LC_NUMERIC, L"C"); - } - - r = wcstod(nptr, endptr); - - if (!saved_locale.empty()) { - wsetlocale(LC_NUMERIC, saved_locale.c_str()); - } - - return r; -} - void builtin_printf_state_t::fatal_error(const wchar_t *fmt, ...) { // Don't error twice. if (early_exit) return; @@ -283,7 +265,12 @@ uintmax_t raw_string_to_scalar_type(const wchar_t *s, wchar_t **end) { template <> long double raw_string_to_scalar_type(const wchar_t *s, wchar_t **end) { - return C_STRTOD(s, end); + // Forcing the locale to C is questionable but it's what the old C_STRTOD() that I inlined here + // as part of changing how locale management is done by fish. + char * old_locale = setlocale(LC_NUMERIC, "C"); + double val = wcstod(s, end); + setlocale(LC_NUMERIC, old_locale); + return val; } template diff --git a/src/common.cpp b/src/common.cpp index 1ad51482a..d0fdccb64 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -470,19 +469,12 @@ wchar_t *quote_end(const wchar_t *pos) { return 0; } -wcstring wsetlocale(int category, const wchar_t *locale) { - char *lang = locale ? wcs2str(locale) : NULL; - char *res = setlocale(category, lang); - free(lang); - +void fish_setlocale() { // Use ellipsis if on known unicode system, otherwise use $. ellipsis_char = (wcwidth(L'\x2026') > 0) ? L'\x2026' : L'$'; // U+23CE is the "return" character omitted_newline_char = (wcwidth(L'\x23CE') > 0) ? L'\x23CE' : L'~'; - - if (!res) return wcstring(); - return format_string(L"%s", res); } bool contains_internal(const wchar_t *a, int vararg_handle, ...) { diff --git a/src/common.h b/src/common.h index 073424362..87fefed34 100644 --- a/src/common.h +++ b/src/common.h @@ -627,10 +627,9 @@ wchar_t *quote_end(const wchar_t *in); /// interactive command executes, to allow new messages to be printed. 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 '$'. -wcstring wsetlocale(int category, const wchar_t *locale); +/// This function should be called after calling `setlocale()` to perform fish specific locale +/// initialization. +void fish_setlocale(); /// Checks if \c needle is included in the list of strings specified. A warning is printed if needle /// is zero. diff --git a/src/env.cpp b/src/env.cpp index 71d858005..84baa76f8 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -168,37 +169,33 @@ static bool var_is_locale(const wcstring &key) { } /// Properly sets all locale information. -static void handle_locale() { - const env_var_t lc_all = env_get_string(L"LC_ALL"); - const wcstring old_locale = wsetlocale(LC_MESSAGES, NULL); +static void handle_locale(const wchar_t *env_var_name) { + debug(2, L"handle_locale() called in response to '%ls' changing", env_var_name); + const char *old_msg_locale = setlocale(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 (int 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()); - } + for (size_t i = 0; locale_variable[i]; i++) { + const wchar_t *key = locale_variable[i]; + const env_var_t var = env_get_string(key); + if (!var.empty()) { + const std::string &name = wcs2string(key); + const std::string &value = wcs2string(var); + setenv(name.c_str(), value.c_str(), 1); + debug(3, L"locale var %s='%s'", name.c_str(), value.c_str()); } } - const wcstring new_locale = wsetlocale(LC_MESSAGES, NULL); - if (old_locale != new_locale) { + char *locale = setlocale(LC_ALL, ""); + fish_setlocale(); + debug(2, L"handle_locale() setlocale(): '%s'", locale); + + const char *new_msg_locale = setlocale(LC_MESSAGES, NULL); + debug(3, L"old LC_MESSAGES locale: '%s'", old_msg_locale); + debug(3, L"new LC_MESSAGES locale: '%s'", new_msg_locale); + if (strcmp(old_msg_locale, new_msg_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. + debug(2, L"changing message locale from '%s' to '%s'", old_msg_locale, new_msg_locale); extern int _nl_msg_cat_cntr; _nl_msg_cat_cntr++; fish_dcgettext("fish", "Changing language to English", LC_MESSAGES); @@ -217,7 +214,8 @@ static bool var_is_curses(const wcstring &key) { /// Push all curses/terminfo env vars into the global environment where they can be found by those /// libraries. -static void handle_curses() { +static void handle_curses(const wchar_t *env_var_name) { + debug(2, L"handle_curses() called in response to '%ls' changing", env_var_name); for (size_t i = 0; curses_variable[i]; i++) { const wchar_t *key = curses_variable[i]; const env_var_t var = env_get_string(key); @@ -225,6 +223,7 @@ static void handle_curses() { const std::string &name = wcs2string(key); const std::string &value = wcs2string(var); setenv(name.c_str(), value.c_str(), 1); + debug(3, L"curses var %s='%s'", name.c_str(), value.c_str()); } } // TODO: Modify input_init() to allow calling it when the terminfo env vars are dynamically @@ -236,9 +235,9 @@ static void handle_curses() { /// React to modifying the given variable. static void react_to_variable_change(const wcstring &key) { if (var_is_locale(key)) { - handle_locale(); + handle_locale(key.c_str()); } else if (var_is_curses(key)) { - handle_curses(); + handle_curses(key.c_str()); } else if (key == L"fish_term256" || key == L"fish_term24bit") { update_fish_color_support(); reader_react_to_color_change(); @@ -852,14 +851,13 @@ void env_push(bool new_scope) { void env_pop() { if (&top->env != global) { int i; - int locale_changed = 0; - + const wchar_t *locale_changed = NULL; 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; + locale_changed = locale_variable[i]; break; } } @@ -881,7 +879,7 @@ void env_pop() { delete killme; - if (locale_changed) handle_locale(); + if (locale_changed) handle_locale(locale_changed); } else { debug(0, _(L"Tried to pop empty environment stack.")); diff --git a/src/fish.cpp b/src/fish.cpp index c4158dde9..8a65d6268 100644 --- a/src/fish.cpp +++ b/src/fish.cpp @@ -418,11 +418,12 @@ int main(int argc, char **argv) { assert(ANY_SENTINAL >= WILDCARD_RESERVED_BASE && ANY_SENTINAL <= WILDCARD_RESERVED_END); assert(R_SENTINAL >= INPUT_COMMON_BASE && R_SENTINAL <= INPUT_COMMON_END); + program_name = L"fish"; set_main_thread(); setup_fork_guards(); - wsetlocale(LC_ALL, L""); - program_name = L"fish"; + setlocale(LC_ALL, ""); + fish_setlocale(); // struct stat tmp; // stat("----------FISH_HIT_MAIN----------", &tmp); diff --git a/src/fish_indent.cpp b/src/fish_indent.cpp index a1479f479..cab9df038 100644 --- a/src/fish_indent.cpp +++ b/src/fish_indent.cpp @@ -328,12 +328,16 @@ static std::string html_colorize(const wcstring &text, static std::string no_colorize(const wcstring &text) { return wcs2string(text); } int main(int argc, char *argv[]) { + program_name = L"fish_indent"; set_main_thread(); setup_fork_guards(); - - wsetlocale(LC_ALL, L""); - program_name = L"fish_indent"; - + // Using the user's default locale could be a problem if it doesn't use UTF-8 encoding. That's + // because the fish project assumes Unicode UTF-8 encoding in all of its scripts. + // + // TODO: Auto-detect the encoding of the script. We should look for a vim style comment + // (e.g., "# vim: set fileencoding=:") or an emacs style comment + // (e.g., "# -*- coding: -*-"). + setlocale(LC_ALL, ""); env_init(); input_init(); diff --git a/src/fish_key_reader.cpp b/src/fish_key_reader.cpp index 1c30c1dbe..89a6ee531 100644 --- a/src/fish_key_reader.cpp +++ b/src/fish_key_reader.cpp @@ -148,8 +148,7 @@ void setup_and_process_keys(bool continuous_mode) { is_interactive_session = 1; // by definition this is interactive set_main_thread(); setup_fork_guards(); - wsetlocale(LC_ALL, L"POSIX"); - program_name = L"fish_key_reader"; + setlocale(LC_ALL, "POSIX"); env_init(); reader_init(); input_init(); @@ -178,6 +177,7 @@ void setup_and_process_keys(bool continuous_mode) { } int main(int argc, char **argv) { + program_name = L"fish_key_reader"; bool continuous_mode = false; const char *short_opts = "+c"; const struct option long_opts[] = {{"continuous", no_argument, NULL, 'd'}, {NULL, 0, NULL, 0}}; diff --git a/tests/test_util.fish b/tests/test_util.fish index 7e8f1fd68..22c478524 100644 --- a/tests/test_util.fish +++ b/tests/test_util.fish @@ -1,4 +1,4 @@ -# vim: set ts=4 sw=4 et: +# vim: set ts=4 sw=4 tw=100 et: # Utilities for the test runners if test "$argv[1]" = (status -f) @@ -19,14 +19,12 @@ function die exit 1 end -# Check if we're running in the test environment. -# If not, set it up and rerun fish with exec. -# The test is whether the special var __fish_is_running_tests -# exists and contains the same value as XDG_CONFIG_HOME. It checks -# the value and not just the presence because we're going to delete -# the config directory later if we're exiting successfully. +# Check if we're running in the test environment. If not, set it up and rerun fish with exec. The +# test is whether the special var __fish_is_running_tests exists and contains the same value as +# XDG_CONFIG_HOME. It checks the value and not just the presence because we're going to delete the +# config directory later if we're exiting successfully. if not set -q __fish_is_running_tests - # set up our test environment and re-run the original script + # Set up our test environment and re-run the original script. set -l script $argv[1] switch $script case '/*' @@ -35,8 +33,11 @@ if not set -q __fish_is_running_tests # path is relative, make it absolute set script $PWD/$script end - set -l IFS # clear IFS so cmd substitution doesn't split - cd (dirname $script); or die + + begin + set -l IFS # clear IFS so cmd substitution doesn't split + cd (dirname $script); or die + end set -lx XDG_DATA_HOME ../test/data rm -rf $XDG_DATA_HOME/fish @@ -52,13 +53,21 @@ if not set -q __fish_is_running_tests printf 'set fish_function_path \'%s/functions\' \'%s/share/functions\'\n' $escaped_config $escaped_parent > $XDG_CONFIG_HOME/fish/config.fish; or die set -xl __fish_is_running_tests $XDG_CONFIG_HOME - # set locale information to be consistent - set -lx LANG C - set -lx LC_ALL '' - for var in ALL COLLATE MESSAGES MONETARY NUMERIC TIME - set -lx LC_$var '' + # Set locale information for consistent tests. Fish should work with a lot of locales but the + # tests assume an english UTF-8 locale unless they explicitly override this default. We do not + # want the users locale to affect the tests since they might, for example, change the wording of + # logged messages. + # + # TODO: set LANG to en_US.UTF-8 so we test the locale message conversions (i.e., gettext). + set -e LANGUAGE + set -x LANG C + # Remove "LC_" env vars from the test environment. + for var in (set -xn) + string match -q 'LC_*' $var + and set -e $var end - set -lx LC_CTYPE en_US.UTF-8 + set -x LC_CTYPE en_US.UTF-8 + exec ../test/root/bin/fish $script $args_for_test_script die 'exec failed' else if test "$__fish_is_running_tests" != "$XDG_CONFIG_HOME" From 9f21e3792aed2ac0c9bbbc94d3e4ff169cc0288a Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sun, 5 Jun 2016 18:52:19 -0700 Subject: [PATCH 357/363] remove dependency on dcgettext() While fixing issue #3110 I noticed there is exactly one place we use dcgettext() and that use is completely unnecessary. So remove it. --- configure.ac | 2 +- src/env.cpp | 10 +++++----- src/fallback.cpp | 41 ----------------------------------------- src/fallback.h | 9 --------- 4 files changed, 6 insertions(+), 56 deletions(-) diff --git a/configure.ac b/configure.ac index 8f1b95fad..620bd3ad8 100644 --- a/configure.ac +++ b/configure.ac @@ -305,7 +305,7 @@ AC_CHECK_FUNCS( futimens clock_gettime ) AC_CHECK_DECL( [mkostemp], [ AC_CHECK_FUNCS([mkostemp]) ] ) if test x$local_gettext != xno; then - AC_CHECK_FUNCS( gettext dcgettext ) + AC_CHECK_FUNCS( gettext ) # # The Makefile also needs to know if we have gettext, so it knows if diff --git a/src/env.cpp b/src/env.cpp index 84baa76f8..c7924aeff 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -9,7 +9,9 @@ #include #include #include +#ifdef HAVE__NL_MSG_CAT_CNTR #include +#endif #include #include #include @@ -191,15 +193,13 @@ static void handle_locale(const wchar_t *env_var_name) { const char *new_msg_locale = setlocale(LC_MESSAGES, NULL); debug(3, L"old LC_MESSAGES locale: '%s'", old_msg_locale); debug(3, L"new LC_MESSAGES locale: '%s'", new_msg_locale); +#ifdef HAVE__NL_MSG_CAT_CNTR if (strcmp(old_msg_locale, new_msg_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. - debug(2, L"changing message locale from '%s' to '%s'", old_msg_locale, new_msg_locale); + // Make change known to GNU gettext. extern int _nl_msg_cat_cntr; _nl_msg_cat_cntr++; - fish_dcgettext("fish", "Changing language to English", LC_MESSAGES); } +#endif } /// Check if the specified variable is a locale variable. diff --git a/src/fallback.cpp b/src/fallback.cpp index 9b7468425..c3ef3462f 100644 --- a/src/fallback.cpp +++ b/src/fallback.cpp @@ -171,7 +171,6 @@ wchar_t *wcsndup(const wchar_t *in, size_t c) { #endif #ifndef HAVE_WCSLCPY - /*$OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $*/ /* @@ -189,7 +188,6 @@ wchar_t *wcsndup(const wchar_t *in, size_t c) { * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ - size_t wcslcpy(wchar_t *dst, const wchar_t *src, size_t siz) { register wchar_t *d = dst; register const wchar_t *s = src; @@ -212,11 +210,9 @@ size_t wcslcpy(wchar_t *dst, const wchar_t *src, size_t siz) { return s - src - 1; // Count does not include NUL. } - #endif #ifndef HAVE_LRAND48_R - int lrand48_r(struct drand48_data *buffer, long int *result) { *result = rand_r(&buffer->seed); return 0; @@ -226,20 +222,16 @@ int srand48_r(long int seedval, struct drand48_data *buffer) { buffer->seed = (unsigned int)seedval; return 0; } - #endif #ifndef HAVE_FUTIMES - int futimes(int fd, const struct timeval *times) { errno = ENOSYS; return -1; } - #endif #if HAVE_GETTEXT - char *fish_gettext(const char *msgid) { return gettext(msgid); ; @@ -250,35 +242,10 @@ char *fish_bindtextdomain(const char *domainname, const char *dirname) { } char *fish_textdomain(const char *domainname) { return textdomain(domainname); } - #else - char *fish_gettext(const char *msgid) { return (char *)msgid; } - char *fish_bindtextdomain(const char *domainname, const char *dirname) { return NULL; } - char *fish_textdomain(const char *domainname) { return NULL; } - -#endif - -#if HAVE_DCGETTEXT - -char *fish_dcgettext(const char *domainname, const char *msgid, int category) { - return dcgettext(domainname, msgid, category); -} - -#else - -char *fish_dcgettext(const char *domainname, const char *msgid, int category) { - return (char *)msgid; -} - -#endif - -#ifndef HAVE__NL_MSG_CAT_CNTR - -int _nl_msg_cat_cntr = 0; - #endif #ifndef HAVE_KILLPG @@ -299,18 +266,12 @@ double nan(char *tagp) { return 0.0 / 0.0; } #endif #if !HAVE_BROKEN_WCWIDTH - int fish_wcwidth(wchar_t wc) { return wcwidth(wc); } - int fish_wcswidth(const wchar_t *str, size_t n) { return wcswidth(str, n); } - #else - static int mk_wcwidth(wchar_t wc); static int mk_wcswidth(const wchar_t *pwcs, size_t n); - int fish_wcwidth(wchar_t wc) { return mk_wcwidth(wc); } - int fish_wcswidth(const wchar_t *str, size_t n) { return mk_wcswidth(str, n); } /* @@ -373,7 +334,6 @@ int fish_wcswidth(const wchar_t *str, size_t n) { return mk_wcswidth(str, n); } * * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c */ - struct interval { int first; int last; @@ -502,5 +462,4 @@ static int mk_wcswidth(const wchar_t *pwcs, size_t n) { } return width; } - #endif // HAVE_BROKEN_WCWIDTH diff --git a/src/fallback.h b/src/fallback.h index 09e301429..7a609b8dd 100644 --- a/src/fallback.h +++ b/src/fallback.h @@ -120,15 +120,6 @@ char *fish_bindtextdomain(const char *domainname, const char *dirname); /// Cover for textdomain(). char *fish_textdomain(const char *domainname); -/// Cover for dcgettext. -char *fish_dcgettext(const char *domainname, const char *msgid, int category); - -#ifndef HAVE__NL_MSG_CAT_CNTR -/// Some gettext implementation use have this variable, and by increasing it, one can tell the -/// system that the translations need to be reloaded. -extern int _nl_msg_cat_cntr; -#endif - #ifndef HAVE_KILLPG /// Send specified signal to specified process group. int killpg(int pgr, int sig); From 1357f5a3647aba1024afe8557695c1a07a81b129 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Sun, 5 Jun 2016 18:46:04 -0700 Subject: [PATCH 358/363] Repair various invalid HeaderDoc comments. Enable build setting to allow Xcode to complain about invalid comments. --- fish.xcodeproj/project.pbxproj | 9 +++++++++ src/complete.cpp | 5 ++--- src/complete.h | 25 +++++++++++++------------ src/exec.cpp | 2 +- src/expand.h | 8 ++++---- src/highlight.h | 4 ++-- src/pager.cpp | 4 ++-- src/parse_util.h | 2 +- src/parser.h | 9 --------- src/postfork.cpp | 2 +- src/reader.cpp | 15 +++++++-------- src/screen.h | 2 +- 12 files changed, 43 insertions(+), 44 deletions(-) diff --git a/fish.xcodeproj/project.pbxproj b/fish.xcodeproj/project.pbxproj index 21cd62e4f..4234a5165 100644 --- a/fish.xcodeproj/project.pbxproj +++ b/fish.xcodeproj/project.pbxproj @@ -1547,6 +1547,8 @@ ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_ENABLE_CPP_EXCEPTIONS = NO; @@ -1560,6 +1562,7 @@ "DOCDIR=L\\\"/usr/local/share/doc\\\"", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_LABEL = YES; @@ -1762,6 +1765,8 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_ENABLE_CPP_EXCEPTIONS = NO; GCC_ENABLE_CPP_RTTI = YES; @@ -1776,6 +1781,7 @@ ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_LABEL = YES; @@ -1795,6 +1801,8 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_ENABLE_CPP_EXCEPTIONS = NO; @@ -1808,6 +1816,7 @@ "DOCDIR=L\\\"/usr/local/share/doc\\\"", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_LABEL = YES; diff --git a/src/complete.cpp b/src/complete.cpp index f686ab313..e23e9ce13 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -652,8 +652,7 @@ static wcstring complete_function_desc(const wcstring &fn) { /// Complete the specified command name. Search for executables in the path, executables defined /// using an absolute path, functions, builtins and directories for implicit cd commands. /// -/// \param cmd the command string to find completions for -/// \param comp the list to add all completions to +/// \param str_cmd the command string to find completions for void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool use_builtin, bool use_command, bool use_implicit_cd) { if (str_cmd.empty()) return; @@ -704,7 +703,7 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool /// \param str The string to complete. /// \param args The list of option arguments to be evaluated. /// \param desc Description of the completion -/// \param comp_out The list into which the results will be inserted +/// \param flags 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) { bool is_autosuggest = (this->type() == COMPLETE_AUTOSUGGEST); diff --git a/src/complete.h b/src/complete.h index 57c743c02..d9203aaf1 100644 --- a/src/complete.h +++ b/src/complete.h @@ -99,6 +99,13 @@ enum { }; typedef uint32_t completion_request_flags_t; +enum complete_option_type_t { + option_type_args_only, // no option + option_type_short, // -x + option_type_single_long, // -foo + option_type_double_long // --foo +}; + /// Add a completion. /// /// All supplied values are copied, they should be freed by or otherwise disposed by the caller. @@ -117,14 +124,12 @@ typedef uint32_t completion_request_flags_t; /// complete -c grep -s d -x -a "read skip recurse" /// /// \param cmd Command to complete. -/// \param cmd_type If cmd_type is PATH, cmd will be interpreted as the absolute +/// \param cmd_is_path If cmd_is_path is true, cmd will be interpreted as the absolute /// path of the program (optionally containing wildcards), otherwise it /// will be interpreted as the command name. -/// \param short_opt The single character name of an option. (-a is a short option, -/// --all and -funroll are long options) -/// \param long_opt The multi character name of an option. (-a is a short option, --all and -/// -funroll are long options) -/// \param long_mode Whether to use old style, single dash long options. +/// \param option The name of an option. +/// \param option_type The type of option: can be option_type_short (-x), +/// option_type_single_long (-foo), option_type_double_long (--bar). /// \param result_mode Whether to search further completions when this completion has been /// succesfully matched. If result_mode is SHARED, any other completions may also be used. If /// result_mode is NO_FILES, file completion should not be used, but other completions may be used. @@ -135,16 +140,12 @@ typedef uint32_t completion_request_flags_t; /// \param condition a command to be run to check it this completion should be used. If \c condition /// is empty, the completion is always used. /// \param flags A set of completion flags -enum complete_option_type_t { - option_type_args_only, // no option - option_type_short, // -x - option_type_single_long, // -foo - option_type_double_long // --foo -}; void complete_add(const wchar_t *cmd, bool cmd_is_path, const wcstring &option, complete_option_type_t option_type, 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); diff --git a/src/exec.cpp b/src/exec.cpp index aefdbc12d..a5bdbc0be 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -315,7 +315,7 @@ static bool io_transmogrify(const io_chain_t &in_chain, io_chain_t *out_chain, /// \param def the code to evaluate, or the empty string if none /// \param node_offset the offset of the node to evalute, or NODE_OFFSET_INVALID /// \param block_type the type of block to push on evaluation -/// \param io the io redirections to be performed on this block +/// \param ios the io redirections to be performed on this block static void internal_exec_helper(parser_t &parser, const wcstring &def, node_offset_t node_offset, enum block_type_t block_type, const io_chain_t &ios) { // If we have a valid node offset, then we must not have a string to execute. diff --git a/src/expand.h b/src/expand.h index 7c82d72e7..2f1dd2a65 100644 --- a/src/expand.h +++ b/src/expand.h @@ -33,7 +33,7 @@ enum { /// Don't generate descriptions. EXPAND_NO_DESCRIPTIONS = 1 << 6, /// Don't expand jobs (but you can still expand processes). This is because - // job expansion is not thread safe. + /// job expansion is not thread safe. EXPAND_SKIP_JOBS = 1 << 7, /// Don't expand home directories. EXPAND_SKIP_HOME_DIRECTORIES = 1 << 8, @@ -46,7 +46,7 @@ enum { /// working directories. EXPAND_SPECIAL_FOR_CD = 1 << 11, /// Do expansions specifically to support external command completions. This means using PATH as - // a list of potential working directories. + /// a list of potential working directories. EXPAND_SPECIAL_FOR_COMMAND = 1 << 12 }; typedef int expand_flags_t; @@ -109,7 +109,7 @@ enum expand_error_t { /// /// \param input The parameter to expand /// \param output The list to which the result will be appended. -/// \param flag Specifies if any expansion pass should be skipped. Legal values are any combination +/// \param flags Specifies if any expansion pass should be skipped. Legal values are any combination /// of EXPAND_SKIP_CMDSUBST EXPAND_SKIP_VARIABLES and EXPAND_SKIP_WILDCARDS /// \param errors Resulting errors, or NULL to ignore /// @@ -123,7 +123,7 @@ __warn_unused expand_error_t expand_string(const wcstring &input, std::vectorcommand_line; long match_highlight_pos = (long)el->position + match_highlight_pos_adjust; diff --git a/src/screen.h b/src/screen.h index 43e256d15..74cda3beb 100644 --- a/src/screen.h +++ b/src/screen.h @@ -149,7 +149,7 @@ class screen_t { /// \param indent the indent to use for the command line /// \param cursor_pos where the cursor is /// \param pager_data any pager data, to append to the screen -/// \param position_is_within_pager whether the position is within the pager line (first line) +/// \param cursor_is_within_pager whether the position is within the pager line (first line) void s_write(screen_t *s, const wcstring &left_prompt, const wcstring &right_prompt, const wcstring &commandline, size_t explicit_len, const highlight_spec_t *colors, const int *indent, size_t cursor_pos, const page_rendering_t &pager_data, From 90ee810c737d3492f38a45425e46e1d6b7cfa9d3 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Sun, 5 Jun 2016 19:24:23 -0700 Subject: [PATCH 359/363] These autoload comment should be HeaderDoc comments. --- src/autoload.h | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/autoload.h b/src/autoload.h index 1d6d4a618..ef55fd94f 100644 --- a/src/autoload.h +++ b/src/autoload.h @@ -11,15 +11,19 @@ #include "common.h" #include "lru.h" -// A struct responsible for recording an attempt to access a file. +/// Recording an attempt to access a file. struct file_access_attempt_t { - time_t mod_time; // 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 - bool stale; // whether the access attempt is stale - int error; // if we could not access the file, the error code + /// modification time of the file + time_t mod_time; + /// when we last checked the file + time_t last_checked; + /// whether we believe we could access this file + bool accessible; + /// whether the access attempt is stale + bool stale; + /// if we could not access the file, the error code + int error; }; - file_access_attempt_t access_file(const wcstring &path, int mode); struct autoload_function_t : public lru_node_t { @@ -29,12 +33,14 @@ struct autoload_function_t : public lru_node_t { is_loaded(false), is_placeholder(false), is_internalized(false) {} - file_access_attempt_t access; // the last access attempt - bool is_loaded; // whether we have actually loaded this function - // Whether we are a placeholder that stands in for "no such function". If this is true, then - // is_loaded must be false. + /// the last access attempt + file_access_attempt_t access; + /// whether we have actually loaded this function + bool is_loaded; + /// Whether we are a placeholder that stands in for "no such function". If this is true, then + /// is_loaded must be false. bool is_placeholder; - // Whether this function came from a builtin "internalized" script. + /// Whether this function came from a builtin "internalized" script. bool is_internalized; }; @@ -45,7 +51,7 @@ struct builtin_script_t { class env_vars_snapshot_t; -// A class that represents a path from which we can autoload, and the autoloaded contents. +/// A class that represents a path from which we can autoload, and the autoloaded contents. class autoload_t : private lru_cache_t { private: // Lock for thread safety. From 2fafb13eaa30981754a8cd0ba8f3c082e3147fa8 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Sun, 5 Jun 2016 21:30:24 -0700 Subject: [PATCH 360/363] Be a bit more consistent and proper. --- fish.xcodeproj/project.pbxproj | 2 - src/autoload.cpp | 19 +++++----- src/autoload.h | 68 +++++++++++++++------------------- src/wcstringutil.h | 14 +++---- 4 files changed, 45 insertions(+), 58 deletions(-) diff --git a/fish.xcodeproj/project.pbxproj b/fish.xcodeproj/project.pbxproj index 4234a5165..721b5a823 100644 --- a/fish.xcodeproj/project.pbxproj +++ b/fish.xcodeproj/project.pbxproj @@ -569,7 +569,6 @@ D0A0854B13B3ACEE0099B651 /* intern.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = intern.cpp; sourceTree = ""; }; D0A0854C13B3ACEE0099B651 /* io.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = io.cpp; sourceTree = ""; }; D0A0854D13B3ACEE0099B651 /* iothread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = iothread.cpp; sourceTree = ""; }; - D0A0854E13B3ACEE0099B651 /* key_reader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = key_reader.cpp; sourceTree = ""; }; D0A0854F13B3ACEE0099B651 /* kill.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = kill.cpp; sourceTree = ""; }; D0A0855113B3ACEE0099B651 /* output.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = output.cpp; sourceTree = ""; }; D0A0855213B3ACEE0099B651 /* parse_util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = parse_util.cpp; sourceTree = ""; }; @@ -805,7 +804,6 @@ D0A0854D13B3ACEE0099B651 /* iothread.cpp */, D0A0851813B3ACEE0099B651 /* kill.h */, D0A0854F13B3ACEE0099B651 /* kill.cpp */, - D0A0854E13B3ACEE0099B651 /* key_reader.cpp */, D03EE83814DF88B200FC7150 /* lru.h */, D0A0851A13B3ACEE0099B651 /* output.h */, D0A0855113B3ACEE0099B651 /* output.cpp */, diff --git a/src/autoload.cpp b/src/autoload.cpp index 9a8ebf408..56292fc6c 100644 --- a/src/autoload.cpp +++ b/src/autoload.cpp @@ -22,7 +22,7 @@ #include "exec.h" #include "wutil.h" // IWYU pragma: keep -// The time before we'll recheck an autoloaded file. +/// 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) { @@ -135,8 +135,9 @@ bool autoload_t::has_tried_loading(const wcstring &cmd) { return func != NULL; } +/// @return Whether this function is stale. +/// Internalized functions can never be stale. 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; } @@ -159,14 +160,12 @@ autoload_function_t *autoload_t::get_autoloaded_function_with_creation(const wcs /// This internal helper function does all the real work. By using two functions, the internal /// function can return on various places in the code, and the caller can take care of various /// cleanup work. -/// -/// cmd: the command name ('grep') -/// really_load: whether to actually parse it as a function, or just check it it exists -/// reload: whether to reload it if it's already loaded -/// path_list: the set of paths to check -/// -/// Result: if really_load is true, returns whether the function was loaded. Otherwise returns -/// whether the function existed. +/// @param cmd the command name ('grep') +/// @param really_load Whether to actually parse it as a function, or just check it it exists +/// @param reload Whether to reload it if it's already loaded +/// @param path_list The set of paths to check +/// @return If really_load is true, returns whether the function was loaded. Otherwise returns +/// whether the function existed. bool autoload_t::locate_file_and_maybe_load_it(const wcstring &cmd, bool really_load, bool reload, const wcstring_list_t &path_list) { // Note that we are NOT locked in this function! diff --git a/src/autoload.h b/src/autoload.h index ef55fd94f..efa8c16d8 100644 --- a/src/autoload.h +++ b/src/autoload.h @@ -11,17 +11,17 @@ #include "common.h" #include "lru.h" -/// Recording an attempt to access a file. +/// Record of an attempt to access a file. struct file_access_attempt_t { - /// modification time of the file + /// Modification time of the file time_t mod_time; - /// when we last checked the file + /// When we last checked the file time_t last_checked; - /// whether we believe we could access this file + /// Whether or not we believe we can access this file bool accessible; - /// whether the access attempt is stale + /// The access attempt is stale bool stale; - /// if we could not access the file, the error code + /// If we cannot access the file, the error code encountered. int error; }; file_access_attempt_t access_file(const wcstring &path, int mode); @@ -33,9 +33,10 @@ struct autoload_function_t : public lru_node_t { is_loaded(false), is_placeholder(false), is_internalized(false) {} - /// the last access attempt + + /// The last access attempt recorded file_access_attempt_t access; - /// whether we have actually loaded this function + /// Have we actually loaded this function? bool is_loaded; /// Whether we are a placeholder that stands in for "no such function". If this is true, then /// is_loaded must be false. @@ -51,24 +52,23 @@ struct builtin_script_t { class env_vars_snapshot_t; -/// A class that represents a path from which we can autoload, and the autoloaded contents. +/// Class representing a path from which we can autoload and the autoloaded contents. class autoload_t : private lru_cache_t { private: - // Lock for thread safety. + /// Lock for thread safety. pthread_mutex_t lock; - // The environment variable name. + /// The environment variable name. const wcstring env_var_name; - // Builtin script array. + /// Builtin script array. const struct builtin_script_t *const builtin_scripts; - // Builtin script count. + /// Builtin script count. const size_t builtin_script_count; - // The path from which we most recently autoloaded. + /// The path from which we most recently autoloaded. wcstring last_path; - // That path, tokenized (split on separators). + /// the most reecently autoloaded path, tokenized (split on separators). wcstring_list_t last_path_tokenized; - - // 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; void remove_all_functions(void) { this->evict_all_nodes(); } @@ -82,39 +82,31 @@ class autoload_t : private lru_cache_t { bool allow_eviction); protected: - // Overridable callback for when a command is removed. + /// Overridable callback for when a command is removed. virtual void command_removed(const wcstring &cmd) {} public: - // Create an autoload_t for the given environment variable name. + /// 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); - virtual ~autoload_t(); // destructor + virtual ~autoload_t(); - // Autoload the specified file, if it exists in the specified path. Do not load it multiple - // times unless its timestamp changes or parse_util_unload is called. - // - // Autoloading one file may unload another. - // - // \param cmd the filename to search for. The suffix '.fish' is always added to this name - // \param on_unload a callback function to run if a suitable file is found, which has not - // already been run. unload will also be called for old files which are unloaded. - // \param reload wheter to recheck file timestamps on already loaded files + /// Autoload the specified file, if it exists in the specified path. Do not load it multiple + /// times unless its timestamp changes or parse_util_unload is called. + /// Autoloading one file may unload another. + /// @param cmd the filename to search for. The suffix '.fish' is always added to this name + /// @param reload wheter to recheck file timestamps on already loaded files int load(const wcstring &cmd, bool reload); - // Check whether we have tried loading the given command. Does not do any I/O. + /// Check whether we have tried loading the given command. Does not do any I/O. bool has_tried_loading(const wcstring &cmd); - // Tell the autoloader that the specified file, in the specified path, is no longer loaded. - // - // \param cmd the filename to search for. The suffix '.fish' is always added to this name - // \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 + /// Tell the autoloader that the specified file, in the specified path, is no longer loaded. + /// Returns non-zero if the file was removed, zero if the file had not yet been loaded int unload(const wcstring &cmd); - // Check whether the given command could be loaded, but do not load it. + /// 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); }; #endif diff --git a/src/wcstringutil.h b/src/wcstringutil.h index ea4489e76..4a86f86e9 100644 --- a/src/wcstringutil.h +++ b/src/wcstringutil.h @@ -7,18 +7,16 @@ #include "common.h" -/// Typedef that represents a range in a wcstring. The first element is the location, the second is -/// the count. +/// @typedef wcstring_range represents a range in a wcstring. +/// The first element is the location, the second is the count. typedef std::pair wcstring_range; /// wcstring equivalent of wcstok(). Supports NUL. For convenience and wcstok() compatibility, the /// first character of each token separator is replaced with NUL. -/// -/// Returns a pair of (pos, count). -/// Returns (npos, npos) when it's done. -/// Returns (pos, npos) when the token is already known to be the final token. -/// -/// Note that the final token may not necessarily return (pos, npos). +/// @return Returns a pair of (pos, count). +/// This will be (npos, npos) when it's done. In the form of (pos, npos) +/// when the token is already known to be the final token. +/// @note The final token may not necessarily return (pos, npos). wcstring_range wcstring_tok(wcstring& str, const wcstring& needle, wcstring_range last = wcstring_range(0, 0)); From 8829bb13649bd4e2828c72907d081f4057075ede Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 8 Jun 2016 13:20:22 +0200 Subject: [PATCH 361/363] Expand string documentation Explain that globs need to match the entire string and a bit about our regular expressions. --- doc_src/string.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/doc_src/string.txt b/doc_src/string.txt index 2306678a0..2996b4565 100644 --- a/doc_src/string.txt +++ b/doc_src/string.txt @@ -44,10 +44,15 @@ The following subcommands are available: - `escape` escapes each STRING such that it can be passed back to `eval` to produce the original argument again. By default, all special characters are escaped, and quotes are used to simplify the output when possible. If `-n` or `--no-quote` is given, the simplifying quoted format is not used. Exit status: 0 if at least one string was escaped, or 1 otherwise. -- `match` tests each STRING against PATTERN and prints matching substrings. Only the first match for each STRING is reported unless `-a` or `--all` is given, in which case all matches are reported. Matching can be made case-insensitive with `-i` or `--ignore-case`. If `-n` or `--index` is given, each match is reported as a 1-based start position and a length. By default, PATTERN is interpreted as a glob pattern matched against each entire STRING argument. If `-r` or `--regex` is given, PATTERN is interpreted as a Perl-compatible regular expression. For a regular expression containing capturing groups, multiple items will be reported for each match, one for the entire match and one for each capturing group. If --invert or -v is used the selected lines will be only those which do not match the given glob pattern or regular expression. Exit status: 0 if at least one match was found, or 1 otherwise. +- `match` tests each STRING against PATTERN and prints matching substrings. Only the first match for each STRING is reported unless `-a` or `--all` is given, in which case all matches are reported. Matching can be made case-insensitive with `-i` or `--ignore-case`. If `-n` or `--index` is given, each match is reported as a 1-based start position and a length. By default, PATTERN is interpreted as a glob pattern matched against each entire STRING argument. A glob pattern is only considered a valid match if it matches the entire STRING. If `-r` or `--regex` is given, PATTERN is interpreted as a Perl-compatible regular expression, which does not have to match the entire STRING. For a regular expression containing capturing groups, multiple items will be reported for each match, one for the entire match and one for each capturing group. If --invert or -v is used the selected lines will be only those which do not match the given glob pattern or regular expression. Exit status: 0 if at least one match was found, or 1 otherwise. - `replace` is similar to `match` but replaces non-overlapping matching substrings with a replacement string and prints the result. By default, PATTERN is treated as a literal substring to be matched. If `-r` or `--regex` is given, PATTERN is interpreted as a Perl-compatible regular expression, and REPLACEMENT can contain C-style escape sequences like `\t` as well as references to capturing groups by number or name as `$n` or `${n}`. Exit status: 0 if at least one replacement was performed, or 1 otherwise. +\subsection regular-expressions Regular Expressions + +Both the `match` and `replace` subcommand support regular expressions when used with the `-r` or `--regex` option. The dialect is that of PCRE2. + +In general, special characters are special by default, so `a+` matches one or more "a"s, while `a\+` matches an "a" and then a "+". `(a+)` matches one or more "a"s in a capturing group (`(?:XXXX)` denotes a non-capturing group). For the replacement parameter of `replace`, `$n` refers to the n-th group of the match. In the match parameter, `\n` (e.g. `\1`) refers back to groups. \subsection string-example Examples From 9d2092bf9fa65450bb8a021c4ecd42af1b7bf11b Mon Sep 17 00:00:00 2001 From: Corey Ford Date: Wed, 8 Jun 2016 16:09:29 -0700 Subject: [PATCH 362/363] don't print header for each job --- src/builtin_jobs.cpp | 2 +- tests/jobs.err | 0 tests/jobs.in | 3 +++ tests/jobs.out | 3 +++ tests/jobs.status | 1 + 5 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 tests/jobs.err create mode 100644 tests/jobs.in create mode 100644 tests/jobs.out create mode 100644 tests/jobs.status diff --git a/src/builtin_jobs.cpp b/src/builtin_jobs.cpp index 406795aa3..f5f7a0592 100644 --- a/src/builtin_jobs.cpp +++ b/src/builtin_jobs.cpp @@ -204,7 +204,7 @@ int builtin_jobs(parser_t &parser, io_streams_t &streams, wchar_t **argv) { while ((j = jobs.next())) { // Ignore unconstructed jobs, i.e. ourself. if ((j->flags & JOB_CONSTRUCTED) && !job_is_completed(j)) { - builtin_jobs_print(j, mode, !streams.out_is_redirected, streams); + builtin_jobs_print(j, mode, !found && !streams.out_is_redirected, streams); found = 1; } } diff --git a/tests/jobs.err b/tests/jobs.err new file mode 100644 index 000000000..e69de29bb diff --git a/tests/jobs.in b/tests/jobs.in new file mode 100644 index 000000000..c32b9b376 --- /dev/null +++ b/tests/jobs.in @@ -0,0 +1,3 @@ +sleep 1 & +sleep 1 & +jobs -c diff --git a/tests/jobs.out b/tests/jobs.out new file mode 100644 index 000000000..702f017fa --- /dev/null +++ b/tests/jobs.out @@ -0,0 +1,3 @@ +Command +sleep +sleep diff --git a/tests/jobs.status b/tests/jobs.status new file mode 100644 index 000000000..573541ac9 --- /dev/null +++ b/tests/jobs.status @@ -0,0 +1 @@ +0 From 222a07e907c239f7dab541512b109730a07828dc Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 10 Jun 2016 14:13:15 +0200 Subject: [PATCH 363/363] Allow compressed man pages in `help` It seems Fedora compresses our whopping 340k of man pages. Fixes #3130. Inspired by @TieDyedDevil's work there. --- share/functions/__fish_print_commands.fish | 8 +++++--- share/functions/__fish_print_help.fish | 8 ++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/share/functions/__fish_print_commands.fish b/share/functions/__fish_print_commands.fish index 21c0876d3..bac2bd732 100644 --- a/share/functions/__fish_print_commands.fish +++ b/share/functions/__fish_print_commands.fish @@ -1,5 +1,7 @@ function __fish_print_commands --description "Print a list of documented fish commands" - if test -d $__fish_datadir/man/man1/ - find $__fish_datadir/man/man1/ -type f -name \*.1 -execdir basename '{}' .1 ';' - end + if test -d $__fish_datadir/man/man1/ + for file in $__fish_datadir/man/man1/**.1* + string replace -r '.*/' '' -- $file | string replace -r '.1(.gz)?$' '' + end + end end diff --git a/share/functions/__fish_print_help.fish b/share/functions/__fish_print_help.fish index 3d5ac0b6a..265f96159 100644 --- a/share/functions/__fish_print_help.fish +++ b/share/functions/__fish_print_help.fish @@ -11,7 +11,7 @@ function __fish_print_help --description "Print help message for the specified f end # Do nothing if the file does not exist - if not test -e "$__fish_datadir/man/man1/$item.1" + if not test -e "$__fish_datadir/man/man1/$item.1" -o -e "$__fish_datadir/man/man1/$item.1.gz" return end @@ -39,7 +39,11 @@ function __fish_print_help --description "Print help message for the specified f set cols (math $cols - 4) # leave a bit of space on the right set rLL -rLL=$cols[1]n end - set help (nroff -man -c -t $rLL "$__fish_datadir/man/man1/$item.1" ^/dev/null) + if test -e "$__fish_datadir/man/man1/$item.1" + set help (nroff -man -c -t $rLL "$__fish_datadir/man/man1/$item.1" ^/dev/null) + else if test -e "$__fish_datadir/man/man1/$item.1.gz" + set help (gunzip -c "$__fish_datadir/man/man1/$item.1.gz" ^/dev/null | nroff -man -c -t $rLL ^/dev/null) + end # The original implementation trimmed off the top 5 lines and bottom 3 lines # from the nroff output. Perhaps that's reliable, but the magic numbers make